summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/acpi/Kconfig9
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/acpica/Makefile4
-rw-r--r--drivers/acpi/acpica/accommon.h1
-rw-r--r--drivers/acpi/acpica/acconfig.h231
-rw-r--r--drivers/acpi/acpica/acdebug.h8
-rw-r--r--drivers/acpi/acpica/acevents.h21
-rw-r--r--drivers/acpi/acpica/acglobal.h11
-rw-r--r--drivers/acpi/acpica/achware.h32
-rw-r--r--drivers/acpi/acpica/aclocal.h1
-rw-r--r--drivers/acpi/acpica/acmacros.h6
-rw-r--r--drivers/acpi/acpica/acnamesp.h5
-rw-r--r--drivers/acpi/acpica/actables.h5
-rw-r--r--drivers/acpi/acpica/evevent.c4
-rw-r--r--drivers/acpi/acpica/evglock.c4
-rw-r--r--drivers/acpi/acpica/evgpe.c4
-rw-r--r--drivers/acpi/acpica/evgpeblk.c4
-rw-r--r--drivers/acpi/acpica/evgpeinit.c4
-rw-r--r--drivers/acpi/acpica/evgpeutil.c3
-rw-r--r--drivers/acpi/acpica/evmisc.c26
-rw-r--r--drivers/acpi/acpica/evsci.c4
-rw-r--r--drivers/acpi/acpica/evxface.c436
-rw-r--r--drivers/acpi/acpica/evxfevnt.c2
-rw-r--r--drivers/acpi/acpica/evxfgpe.c2
-rw-r--r--drivers/acpi/acpica/hwacpi.c3
-rw-r--r--drivers/acpi/acpica/hwesleep.c247
-rw-r--r--drivers/acpi/acpica/hwgpe.c4
-rw-r--r--drivers/acpi/acpica/hwregs.c16
-rw-r--r--drivers/acpi/acpica/hwsleep.c401
-rw-r--r--drivers/acpi/acpica/hwtimer.c2
-rw-r--r--drivers/acpi/acpica/hwxface.c50
-rw-r--r--drivers/acpi/acpica/hwxfsleep.c431
-rw-r--r--drivers/acpi/acpica/nsdump.c15
-rw-r--r--drivers/acpi/acpica/nsdumpdv.c2
-rw-r--r--drivers/acpi/acpica/nspredef.c4
-rw-r--r--drivers/acpi/acpica/nsrepair.c159
-rw-r--r--drivers/acpi/acpica/nsutils.c2
-rw-r--r--drivers/acpi/acpica/tbfadt.c8
-rw-r--r--drivers/acpi/acpica/tbinstal.c117
-rw-r--r--drivers/acpi/acpica/tbutils.c95
-rw-r--r--drivers/acpi/acpica/utdecode.c34
-rw-r--r--drivers/acpi/acpica/utglobal.c9
-rw-r--r--drivers/acpi/acpica/utinit.c37
-rw-r--r--drivers/acpi/acpica/utxface.c6
-rw-r--r--drivers/acpi/apei/apei-base.c61
-rw-r--r--drivers/acpi/apei/cper.c2
-rw-r--r--drivers/acpi/apei/einj.c17
-rw-r--r--drivers/acpi/apei/erst.c2
-rw-r--r--drivers/acpi/bgrt.c175
-rw-r--r--drivers/acpi/bus.c1
-rw-r--r--drivers/acpi/ec.c18
-rw-r--r--drivers/acpi/nvs.c4
-rw-r--r--drivers/acpi/osl.c124
-rw-r--r--drivers/acpi/power.c166
-rw-r--r--drivers/acpi/processor_driver.c63
-rw-r--r--drivers/acpi/processor_idle.c34
-rw-r--r--drivers/acpi/processor_thermal.c45
-rw-r--r--drivers/acpi/processor_throttling.c5
-rw-r--r--drivers/acpi/reboot.c3
-rw-r--r--drivers/acpi/scan.c12
-rw-r--r--drivers/acpi/sleep.c76
-rw-r--r--drivers/acpi/thermal.c8
-rw-r--r--drivers/acpi/video.c50
-rw-r--r--drivers/acpi/video_detect.c2
-rw-r--r--drivers/atm/eni.c1
-rw-r--r--drivers/atm/firestream.c1
-rw-r--r--drivers/atm/horizon.c1
-rw-r--r--drivers/atm/idt77105.c1
-rw-r--r--drivers/atm/iphase.c1
-rw-r--r--drivers/atm/suni.c1
-rw-r--r--drivers/atm/zatm.c1
-rw-r--r--drivers/base/dma-buf.c165
-rw-r--r--drivers/block/floppy.c1
-rw-r--r--drivers/block/hd.c1
-rw-r--r--drivers/block/nbd.c296
-rw-r--r--drivers/block/rbd.c730
-rw-r--r--drivers/block/rbd_types.h4
-rw-r--r--drivers/block/sunvdc.c5
-rw-r--r--drivers/block/xd.c1
-rw-r--r--drivers/bluetooth/bt3c_cs.c1
-rw-r--r--drivers/bluetooth/btuart_cs.c1
-rw-r--r--drivers/bluetooth/dtl1_cs.c1
-rw-r--r--drivers/char/apm-emulation.c1
-rw-r--r--drivers/char/ds1302.c1
-rw-r--r--drivers/char/efirtc.c1
-rw-r--r--drivers/char/genrtc.c1
-rw-r--r--drivers/char/hpet.c1
-rw-r--r--drivers/char/ipmi/ipmi_devintf.c1
-rw-r--r--drivers/char/ipmi/ipmi_kcs_sm.c4
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c243
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c73
-rw-r--r--drivers/char/ipmi/ipmi_watchdog.c25
-rw-r--r--drivers/char/lp.c6
-rw-r--r--drivers/char/mbcs.c1
-rw-r--r--drivers/char/mspec.c1
-rw-r--r--drivers/char/mwave/3780i.c1
-rw-r--r--drivers/char/nvram.c1
-rw-r--r--drivers/char/nwflash.c1
-rw-r--r--drivers/char/pcmcia/synclink_cs.c1
-rw-r--r--drivers/char/rtc.c1
-rw-r--r--drivers/char/sonypi.c1
-rw-r--r--drivers/char/xilinx_hwicap/xilinx_hwicap.c1
-rw-r--r--drivers/clk/Kconfig37
-rw-r--r--drivers/clk/Makefile2
-rw-r--r--drivers/clk/clk-divider.c200
-rw-r--r--drivers/clk/clk-fixed-rate.c82
-rw-r--r--drivers/clk/clk-gate.c150
-rw-r--r--drivers/clk/clk-mux.c116
-rw-r--r--drivers/clk/clk.c1461
-rw-r--r--drivers/clocksource/Kconfig1
-rw-r--r--drivers/cpufreq/db8500-cpufreq.c15
-rw-r--r--drivers/cpufreq/omap-cpufreq.c1
-rw-r--r--drivers/cpufreq/powernow-k7.c1
-rw-r--r--drivers/cpuidle/cpuidle.c97
-rw-r--r--drivers/cpuidle/driver.c2
-rw-r--r--drivers/cpuidle/governors/menu.c7
-rw-r--r--drivers/cpuidle/sysfs.c40
-rw-r--r--drivers/dma/Kconfig12
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/amba-pl08x.c46
-rw-r--r--drivers/dma/at_hdmac.c111
-rw-r--r--drivers/dma/at_hdmac_regs.h34
-rw-r--r--drivers/dma/coh901318.c41
-rw-r--r--drivers/dma/dmaengine.c8
-rw-r--r--drivers/dma/dmaengine.h89
-rw-r--r--drivers/dma/dw_dmac.c228
-rw-r--r--drivers/dma/dw_dmac_regs.h16
-rw-r--r--drivers/dma/ep93xx_dma.c31
-rw-r--r--drivers/dma/fsldma.c28
-rw-r--r--drivers/dma/fsldma.h1
-rw-r--r--drivers/dma/imx-dma.c950
-rw-r--r--drivers/dma/imx-sdma.c187
-rw-r--r--drivers/dma/intel_mid_dma.c46
-rw-r--r--drivers/dma/intel_mid_dma_regs.h2
-rw-r--r--drivers/dma/ioat/dma.c21
-rw-r--r--drivers/dma/ioat/dma.h23
-rw-r--r--drivers/dma/ioat/dma_v2.c13
-rw-r--r--drivers/dma/ioat/dma_v3.c12
-rw-r--r--drivers/dma/iop-adma.c52
-rw-r--r--drivers/dma/ipu/ipu_idmac.c25
-rw-r--r--drivers/dma/mpc512x_dma.c25
-rw-r--r--drivers/dma/mv_xor.c34
-rw-r--r--drivers/dma/mv_xor.h3
-rw-r--r--drivers/dma/mxs-dma.c60
-rw-r--r--drivers/dma/pch_dma.c37
-rw-r--r--drivers/dma/pl330.c2149
-rw-r--r--drivers/dma/ppc4xx/adma.c49
-rw-r--r--drivers/dma/ppc4xx/adma.h2
-rw-r--r--drivers/dma/sa11x0-dma.c1109
-rw-r--r--drivers/dma/shdma.c33
-rw-r--r--drivers/dma/shdma.h1
-rw-r--r--drivers/dma/sirf-dma.c27
-rw-r--r--drivers/dma/ste_dma40.c41
-rw-r--r--drivers/dma/timb_dma.c37
-rw-r--r--drivers/dma/txx9dmac.c43
-rw-r--r--drivers/dma/txx9dmac.h1
-rw-r--r--drivers/edac/Kconfig2
-rw-r--r--drivers/edac/edac_mc.c6
-rw-r--r--drivers/edac/i5100_edac.c13
-rw-r--r--drivers/edac/i5400_edac.c54
-rw-r--r--drivers/edac/ppc4xx_edac.c4
-rw-r--r--drivers/edac/sb_edac.c52
-rw-r--r--drivers/firewire/core-cdev.c1
-rw-r--r--drivers/firewire/core-device.c1
-rw-r--r--drivers/firewire/core-topology.c1
-rw-r--r--drivers/firewire/ohci.c1
-rw-r--r--drivers/firewire/sbp2.c1
-rw-r--r--drivers/gpio/Kconfig14
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/gpio-davinci.c26
-rw-r--r--drivers/gpio/gpio-ep93xx.c8
-rw-r--r--drivers/gpio/gpio-lpc32xx.c19
-rw-r--r--drivers/gpio/gpio-mc9s08dz60.c161
-rw-r--r--drivers/gpio/gpio-omap.c291
-rw-r--r--drivers/gpio/gpio-pl061.c7
-rw-r--r--drivers/gpio/gpio-pxa.c2
-rw-r--r--drivers/gpio/gpio-sa1100.c1
-rw-r--r--drivers/gpio/gpio-samsung.c487
-rw-r--r--drivers/gpio/gpio-sodaville.c302
-rw-r--r--drivers/gpio/gpio-stmpe.c43
-rw-r--r--drivers/gpio/gpio-tegra.c4
-rw-r--r--drivers/gpio/gpio-tps65910.c20
-rw-r--r--drivers/gpio/gpio-twl4030.c111
-rw-r--r--drivers/gpio/gpiolib.c98
-rw-r--r--drivers/gpu/drm/gma500/mdfld_dsi_output.c5
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c7
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h2
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_i2c.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_state.c2
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c8
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c3
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c4
-rw-r--r--drivers/gpu/drm/radeon/evergreen_cs.c98
-rw-r--r--drivers/gpu/drm/radeon/evergreend.h8
-rw-r--r--drivers/gpu/drm/radeon/r600_cs.c391
-rw-r--r--drivers/gpu/drm/radeon/r600d.h8
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c12
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/cayman1
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/evergreen1
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/r6001
-rw-r--r--drivers/hsi/Kconfig19
-rw-r--r--drivers/hsi/Makefile6
-rw-r--r--drivers/hsi/clients/Kconfig13
-rw-r--r--drivers/hsi/clients/Makefile5
-rw-r--r--drivers/hsi/clients/hsi_char.c802
-rw-r--r--drivers/hsi/hsi.c494
-rw-r--r--drivers/hsi/hsi_boardinfo.c62
-rw-r--r--drivers/hsi/hsi_core.h35
-rw-r--r--drivers/hwmon/fschmd.c4
-rw-r--r--drivers/hwmon/mc13783-adc.c2
-rw-r--r--drivers/hwmon/w83793.c4
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c5
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c3
-rw-r--r--drivers/i2c/algos/i2c-algo-pcf.c3
-rw-r--r--drivers/i2c/algos/i2c-algo-pcf.h3
-rw-r--r--drivers/i2c/busses/Kconfig1
-rw-r--r--drivers/i2c/busses/i2c-acorn.c1
-rw-r--r--drivers/i2c/busses/i2c-gpio.c98
-rw-r--r--drivers/i2c/busses/i2c-i801.c27
-rw-r--r--drivers/i2c/busses/i2c-isch.c10
-rw-r--r--drivers/i2c/i2c-boardinfo.c3
-rw-r--r--drivers/i2c/i2c-core.c3
-rw-r--r--drivers/i2c/i2c-core.h3
-rw-r--r--drivers/i2c/i2c-dev.c3
-rw-r--r--drivers/i2c/i2c-smbus.c3
-rw-r--r--drivers/i2c/muxes/pca9541.c13
-rw-r--r--drivers/i2c/muxes/pca954x.c13
-rw-r--r--drivers/ide/ide-cs.c1
-rw-r--r--drivers/ide/qd65xx.c1
-rw-r--r--drivers/infiniband/hw/ehca/ehca_reqs.c1
-rw-r--r--drivers/input/input-compat.c4
-rw-r--r--drivers/input/input-compat.h2
-rw-r--r--drivers/input/joydev.c1
-rw-r--r--drivers/input/joystick/amijoy.c4
-rw-r--r--drivers/input/keyboard/gpio_keys.c259
-rw-r--r--drivers/input/keyboard/jornada720_kbd.c1
-rw-r--r--drivers/input/keyboard/tegra-kbc.c1
-rw-r--r--drivers/input/misc/88pm860x_onkey.c26
-rw-r--r--drivers/input/mouse/amimouse.c1
-rw-r--r--drivers/input/mouse/atarimouse.c1
-rw-r--r--drivers/input/mouse/sentelic.c294
-rw-r--r--drivers/input/mouse/sentelic.h35
-rw-r--r--drivers/input/serio/ams_delta_serio.c2
-rw-r--r--drivers/input/serio/hp_sdc.c1
-rw-r--r--drivers/input/serio/maceps2.c1
-rw-r--r--drivers/input/serio/rpckbd.c45
-rw-r--r--drivers/input/serio/sa1111ps2.c60
-rw-r--r--drivers/input/tablet/Kconfig1
-rw-r--r--drivers/input/tablet/wacom.h9
-rw-r--r--drivers/input/tablet/wacom_sys.c231
-rw-r--r--drivers/input/tablet/wacom_wac.c49
-rw-r--r--drivers/input/tablet/wacom_wac.h6
-rw-r--r--drivers/input/touchscreen/jornada720_ts.c1
-rw-r--r--drivers/input/touchscreen/mc13783_ts.c11
-rw-r--r--drivers/isdn/hardware/avm/avm_cs.c1
-rw-r--r--drivers/isdn/hisax/avma1_cs.c1
-rw-r--r--drivers/isdn/hisax/elsa_cs.c1
-rw-r--r--drivers/isdn/hisax/sedlbauer_cs.c1
-rw-r--r--drivers/isdn/hisax/teles_cs.c1
-rw-r--r--drivers/isdn/i4l/isdn_bsdcomp.c1
-rw-r--r--drivers/isdn/pcbit/layer2.c1
-rw-r--r--drivers/leds/leds-88pm860x.c23
-rw-r--r--drivers/macintosh/macio-adb.c1
-rw-r--r--drivers/macintosh/therm_adt746x.c1
-rw-r--r--drivers/macintosh/therm_pm72.c1
-rw-r--r--drivers/macintosh/therm_windtunnel.c1
-rw-r--r--drivers/macintosh/via-cuda.c1
-rw-r--r--drivers/macintosh/via-macii.c1
-rw-r--r--drivers/macintosh/via-pmu.c1
-rw-r--r--drivers/macintosh/via-pmu68k.c1
-rw-r--r--drivers/macintosh/windfarm_lm75_sensor.c1
-rw-r--r--drivers/macintosh/windfarm_pm121.c1
-rw-r--r--drivers/macintosh/windfarm_pm81.c1
-rw-r--r--drivers/macintosh/windfarm_pm91.c1
-rw-r--r--drivers/macintosh/windfarm_smu_controls.c1
-rw-r--r--drivers/macintosh/windfarm_smu_sensors.c1
-rw-r--r--drivers/md/Kconfig28
-rw-r--r--drivers/md/Makefile1
-rw-r--r--drivers/md/dm-bufio.c108
-rw-r--r--drivers/md/dm-bufio.h8
-rw-r--r--drivers/md/dm-crypt.c46
-rw-r--r--drivers/md/dm-delay.c9
-rw-r--r--drivers/md/dm-exception-store.c2
-rw-r--r--drivers/md/dm-flakey.c3
-rw-r--r--drivers/md/dm-ioctl.c5
-rw-r--r--drivers/md/dm-linear.c3
-rw-r--r--drivers/md/dm-log.c3
-rw-r--r--drivers/md/dm-mpath.c52
-rw-r--r--drivers/md/dm-queue-length.c3
-rw-r--r--drivers/md/dm-raid.c53
-rw-r--r--drivers/md/dm-raid1.c12
-rw-r--r--drivers/md/dm-round-robin.c3
-rw-r--r--drivers/md/dm-service-time.c5
-rw-r--r--drivers/md/dm-stripe.c3
-rw-r--r--drivers/md/dm-table.c9
-rw-r--r--drivers/md/dm-thin-metadata.c5
-rw-r--r--drivers/md/dm-thin-metadata.h13
-rw-r--r--drivers/md/dm-thin.c680
-rw-r--r--drivers/md/dm-verity.c913
-rw-r--r--drivers/md/dm.c1
-rw-r--r--drivers/md/persistent-data/dm-btree-internal.h7
-rw-r--r--drivers/md/persistent-data/dm-btree-remove.c202
-rw-r--r--drivers/md/persistent-data/dm-btree.c27
-rw-r--r--drivers/md/persistent-data/dm-space-map-common.c3
-rw-r--r--drivers/media/dvb/dvb-core/dmxdev.c1
-rw-r--r--drivers/media/dvb/firewire/firedtv-fw.c1
-rw-r--r--drivers/media/dvb/ttpci/av7110.c1
-rw-r--r--drivers/media/media-devnode.c1
-rw-r--r--drivers/media/video/davinci/vpbe_osd.c1
-rw-r--r--drivers/media/video/davinci/vpbe_venc.c1
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h1
-rw-r--r--drivers/media/video/mx3_camera.c2
-rw-r--r--drivers/media/video/timblogiw.c2
-rw-r--r--drivers/media/video/v4l2-common.c1
-rw-r--r--drivers/media/video/v4l2-dev.c1
-rw-r--r--drivers/message/i2o/i2o_scsi.c1
-rw-r--r--drivers/mfd/88pm860x-core.c110
-rw-r--r--drivers/mfd/88pm860x-i2c.c25
-rw-r--r--drivers/mfd/Kconfig59
-rw-r--r--drivers/mfd/Makefile4
-rw-r--r--drivers/mfd/ab8500-core.c375
-rw-r--r--drivers/mfd/ab8500-i2c.c30
-rw-r--r--drivers/mfd/anatop-mfd.c137
-rw-r--r--drivers/mfd/asic3.c6
-rw-r--r--drivers/mfd/da9052-core.c3
-rw-r--r--drivers/mfd/da9052-i2c.c11
-rw-r--r--drivers/mfd/da9052-spi.c9
-rw-r--r--drivers/mfd/db8500-prcmu.c1220
-rw-r--r--drivers/mfd/dbx500-prcmu-regs.h130
-rw-r--r--drivers/mfd/mc13xxx-core.c11
-rw-r--r--drivers/mfd/mcp-core.c51
-rw-r--r--drivers/mfd/mcp-sa11x0.c199
-rw-r--r--drivers/mfd/mfd-core.c2
-rw-r--r--drivers/mfd/omap-usb-host.c7
-rw-r--r--drivers/mfd/pcf50633-core.c8
-rw-r--r--drivers/mfd/pcf50633-gpio.c27
-rw-r--r--drivers/mfd/pcf50633-irq.c7
-rw-r--r--drivers/mfd/rc5t583-irq.c408
-rw-r--r--drivers/mfd/rc5t583.c386
-rw-r--r--drivers/mfd/s5m-core.c58
-rw-r--r--drivers/mfd/s5m-irq.c14
-rw-r--r--drivers/mfd/sm501.c10
-rw-r--r--drivers/mfd/stmpe.c134
-rw-r--r--drivers/mfd/tps65090.c387
-rw-r--r--drivers/mfd/tps65217.c242
-rw-r--r--drivers/mfd/tps65910-irq.c11
-rw-r--r--drivers/mfd/tps65910.c123
-rw-r--r--drivers/mfd/twl-core.c153
-rw-r--r--drivers/mfd/twl-core.h4
-rw-r--r--drivers/mfd/twl4030-irq.c107
-rw-r--r--drivers/mfd/twl6030-irq.c86
-rw-r--r--drivers/mfd/ucb1x00-assabet.c46
-rw-r--r--drivers/mfd/ucb1x00-core.c433
-rw-r--r--drivers/mfd/ucb1x00-ts.c39
-rw-r--r--drivers/mfd/wm831x-spi.c2
-rw-r--r--drivers/mfd/wm8400-core.c3
-rw-r--r--drivers/mfd/wm8994-core.c2
-rw-r--r--drivers/mfd/wm8994-regmap.c20
-rw-r--r--drivers/misc/sgi-gru/gru_instructions.h1
-rw-r--r--drivers/misc/sgi-xp/xp.h1
-rw-r--r--drivers/mmc/card/block.c22
-rw-r--r--drivers/mmc/core/cd-gpio.c13
-rw-r--r--drivers/mmc/core/core.c252
-rw-r--r--drivers/mmc/core/host.c1
-rw-r--r--drivers/mmc/core/host.h1
-rw-r--r--drivers/mmc/core/mmc.c55
-rw-r--r--drivers/mmc/core/mmc_ops.c12
-rw-r--r--drivers/mmc/host/Kconfig25
-rw-r--r--drivers/mmc/host/Makefile2
-rw-r--r--drivers/mmc/host/atmel-mci.c23
-rw-r--r--drivers/mmc/host/davinci_mmc.c66
-rw-r--r--drivers/mmc/host/dw_mmc-pci.c158
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c134
-rw-r--r--drivers/mmc/host/dw_mmc.c280
-rw-r--r--drivers/mmc/host/dw_mmc.h7
-rw-r--r--drivers/mmc/host/mmci.c4
-rw-r--r--drivers/mmc/host/mxcmmc.c5
-rw-r--r--drivers/mmc/host/mxs-mmc.c14
-rw-r--r--drivers/mmc/host/omap_hsmmc.c293
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c37
-rw-r--r--drivers/mmc/host/sdhci-pci.c47
-rw-r--r--drivers/mmc/host/sdhci-spear.c9
-rw-r--r--drivers/mmc/host/sdhci-tegra.c100
-rw-r--r--drivers/mmc/host/sdhci.c38
-rw-r--r--drivers/mmc/host/sdhci.h2
-rw-r--r--drivers/mmc/host/sh_mmcif.c6
-rw-r--r--drivers/mmc/host/sh_mobile_sdhi.c29
-rw-r--r--drivers/mmc/host/tmio_mmc.h9
-rw-r--r--drivers/mmc/host/tmio_mmc_dma.c4
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c108
-rw-r--r--drivers/mtd/Kconfig5
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c83
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c283
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0020.c33
-rw-r--r--drivers/mtd/chips/cfi_util.c6
-rw-r--r--drivers/mtd/chips/fwh_lock.h4
-rw-r--r--drivers/mtd/chips/map_absent.c10
-rw-r--r--drivers/mtd/chips/map_ram.c14
-rw-r--r--drivers/mtd/chips/map_rom.c13
-rw-r--r--drivers/mtd/devices/Kconfig8
-rw-r--r--drivers/mtd/devices/Makefile1
-rw-r--r--drivers/mtd/devices/block2mtd.c28
-rw-r--r--drivers/mtd/devices/doc2000.c25
-rw-r--r--drivers/mtd/devices/doc2001.c22
-rw-r--r--drivers/mtd/devices/doc2001plus.c22
-rw-r--r--drivers/mtd/devices/docg3.c201
-rw-r--r--drivers/mtd/devices/docg3.h20
-rw-r--r--drivers/mtd/devices/lart.c17
-rw-r--r--drivers/mtd/devices/m25p80.c56
-rw-r--r--drivers/mtd/devices/ms02-nv.c12
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c50
-rw-r--r--drivers/mtd/devices/mtdram.c35
-rw-r--r--drivers/mtd/devices/phram.c76
-rw-r--r--drivers/mtd/devices/pmc551.c100
-rw-r--r--drivers/mtd/devices/slram.c42
-rw-r--r--drivers/mtd/devices/spear_smi.c1147
-rw-r--r--drivers/mtd/devices/sst25l.c46
-rw-r--r--drivers/mtd/inftlcore.c2
-rw-r--r--drivers/mtd/lpddr/lpddr_cmds.c37
-rw-r--r--drivers/mtd/maps/Kconfig1
-rw-r--r--drivers/mtd/maps/bfin-async-flash.c4
-rw-r--r--drivers/mtd/maps/dc21285.c2
-rw-r--r--drivers/mtd/maps/gpio-addr-flash.c4
-rw-r--r--drivers/mtd/maps/h720x-flash.c4
-rw-r--r--drivers/mtd/maps/impa7.c2
-rw-r--r--drivers/mtd/maps/intel_vr_nor.c2
-rw-r--r--drivers/mtd/maps/ixp2000.c2
-rw-r--r--drivers/mtd/maps/ixp4xx.c5
-rw-r--r--drivers/mtd/maps/l440gx.c14
-rw-r--r--drivers/mtd/maps/lantiq-flash.c6
-rw-r--r--drivers/mtd/maps/latch-addr-flash.c5
-rw-r--r--drivers/mtd/maps/pcmciamtd.c14
-rw-r--r--drivers/mtd/maps/physmap.c24
-rw-r--r--drivers/mtd/maps/plat-ram.c5
-rw-r--r--drivers/mtd/maps/pxa2xx-flash.c3
-rw-r--r--drivers/mtd/maps/rbtx4939-flash.c4
-rw-r--r--drivers/mtd/maps/sa1100-flash.c130
-rw-r--r--drivers/mtd/maps/solutionengine.c4
-rw-r--r--drivers/mtd/maps/uclinux.c2
-rw-r--r--drivers/mtd/maps/vmu-flash.c14
-rw-r--r--drivers/mtd/maps/wr_sbc82xx_flash.c2
-rw-r--r--drivers/mtd/mtd_blkdevs.c1
-rw-r--r--drivers/mtd/mtdblock.c8
-rw-r--r--drivers/mtd/mtdchar.c57
-rw-r--r--drivers/mtd/mtdconcat.c106
-rw-r--r--drivers/mtd/mtdcore.c271
-rw-r--r--drivers/mtd/mtdoops.c9
-rw-r--r--drivers/mtd/mtdpart.c200
-rw-r--r--drivers/mtd/nand/Kconfig23
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/alauda.c9
-rw-r--r--drivers/mtd/nand/atmel_nand.c137
-rw-r--r--drivers/mtd/nand/bcm_umi_nand.c11
-rw-r--r--drivers/mtd/nand/bf5xx_nand.c2
-rw-r--r--drivers/mtd/nand/cafe_nand.c3
-rw-r--r--drivers/mtd/nand/cmx270_nand.c2
-rw-r--r--drivers/mtd/nand/cs553x_nand.c4
-rw-r--r--drivers/mtd/nand/davinci_nand.c5
-rw-r--r--drivers/mtd/nand/denali.c3
-rw-r--r--drivers/mtd/nand/diskonchip.c1
-rw-r--r--drivers/mtd/nand/docg4.c1377
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c6
-rw-r--r--drivers/mtd/nand/fsmc_nand.c924
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-lib.c43
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.c14
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.h2
-rw-r--r--drivers/mtd/nand/h1910.c4
-rw-r--r--drivers/mtd/nand/jz4740_nand.c11
-rw-r--r--drivers/mtd/nand/mxc_nand.c11
-rw-r--r--drivers/mtd/nand/nand_base.c194
-rw-r--r--drivers/mtd/nand/ndfc.c1
-rw-r--r--drivers/mtd/nand/omap2.c5
-rw-r--r--drivers/mtd/nand/orion_nand.c4
-rw-r--r--drivers/mtd/nand/plat_nand.c5
-rw-r--r--drivers/mtd/nand/ppchameleonevb.c18
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c6
-rw-r--r--drivers/mtd/nand/r852.c1
-rw-r--r--drivers/mtd/nand/rtc_from4.c1
-rw-r--r--drivers/mtd/nand/s3c2410.c5
-rw-r--r--drivers/mtd/nand/sh_flctl.c106
-rw-r--r--drivers/mtd/nand/sharpsl.c5
-rw-r--r--drivers/mtd/nand/tmio_nand.c7
-rw-r--r--drivers/mtd/nand/txx9ndfmc.c3
-rw-r--r--drivers/mtd/nftlcore.c7
-rw-r--r--drivers/mtd/onenand/Kconfig1
-rw-r--r--drivers/mtd/onenand/generic.c6
-rw-r--r--drivers/mtd/onenand/omap2.c6
-rw-r--r--drivers/mtd/onenand/onenand_base.c68
-rw-r--r--drivers/mtd/onenand/samsung.c6
-rw-r--r--drivers/mtd/redboot.c6
-rw-r--r--drivers/mtd/sm_ftl.c2
-rw-r--r--drivers/mtd/ubi/gluebi.c29
-rw-r--r--drivers/net/appletalk/cops.c1
-rw-r--r--drivers/net/appletalk/ltpc.c1
-rw-r--r--drivers/net/arcnet/com20020_cs.c1
-rw-r--r--drivers/net/bonding/bond_main.c1
-rw-r--r--drivers/net/can/slcan.c1
-rw-r--r--drivers/net/cris/eth_v10.c1
-rw-r--r--drivers/net/ethernet/3com/3c574_cs.c1
-rw-r--r--drivers/net/ethernet/3com/3c589_cs.c1
-rw-r--r--drivers/net/ethernet/8390/3c503.c1
-rw-r--r--drivers/net/ethernet/8390/ac3200.c1
-rw-r--r--drivers/net/ethernet/8390/apne.c1
-rw-r--r--drivers/net/ethernet/8390/ax88796.c1
-rw-r--r--drivers/net/ethernet/8390/axnet_cs.c1
-rw-r--r--drivers/net/ethernet/8390/e2100.c1
-rw-r--r--drivers/net/ethernet/8390/es3210.c1
-rw-r--r--drivers/net/ethernet/8390/etherh.c2
-rw-r--r--drivers/net/ethernet/8390/hp-plus.c1
-rw-r--r--drivers/net/ethernet/8390/hp.c1
-rw-r--r--drivers/net/ethernet/8390/lib8390.c1
-rw-r--r--drivers/net/ethernet/8390/lne390.c1
-rw-r--r--drivers/net/ethernet/8390/mac8390.c1
-rw-r--r--drivers/net/ethernet/8390/ne-h8300.c1
-rw-r--r--drivers/net/ethernet/8390/ne.c1
-rw-r--r--drivers/net/ethernet/8390/ne2.c1
-rw-r--r--drivers/net/ethernet/8390/ne2k-pci.c1
-rw-r--r--drivers/net/ethernet/8390/ne3210.c1
-rw-r--r--drivers/net/ethernet/8390/pcnet_cs.c1
-rw-r--r--drivers/net/ethernet/8390/smc-mca.c1
-rw-r--r--drivers/net/ethernet/8390/smc-ultra.c1
-rw-r--r--drivers/net/ethernet/8390/smc-ultra32.c1
-rw-r--r--drivers/net/ethernet/8390/stnic.c1
-rw-r--r--drivers/net/ethernet/8390/wd.c1
-rw-r--r--drivers/net/ethernet/8390/zorro8390.c1
-rw-r--r--drivers/net/ethernet/alteon/acenic.c1
-rw-r--r--drivers/net/ethernet/amd/7990.c1
-rw-r--r--drivers/net/ethernet/amd/am79c961a.c1
-rw-r--r--drivers/net/ethernet/amd/amd8111e.c1
-rw-r--r--drivers/net/ethernet/amd/declance.c1
-rw-r--r--drivers/net/ethernet/amd/hplance.c1
-rw-r--r--drivers/net/ethernet/amd/mvme147.c1
-rw-r--r--drivers/net/ethernet/amd/nmclan_cs.c1
-rw-r--r--drivers/net/ethernet/amd/sunlance.c1
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c1
-rw-r--r--drivers/net/ethernet/cirrus/cs89x0.c1
-rw-r--r--drivers/net/ethernet/cirrus/mac89x0.c1
-rw-r--r--drivers/net/ethernet/dlink/de600.c1
-rw-r--r--drivers/net/ethernet/dlink/de620.c1
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.c6
-rw-r--r--drivers/net/ethernet/fujitsu/at1700.c1
-rw-r--r--drivers/net/ethernet/fujitsu/eth16i.c1
-rw-r--r--drivers/net/ethernet/fujitsu/fmvj18x_cs.c1
-rw-r--r--drivers/net/ethernet/i825xx/3c507.c1
-rw-r--r--drivers/net/ethernet/i825xx/3c527.c1
-rw-r--r--drivers/net/ethernet/i825xx/eepro.c1
-rw-r--r--drivers/net/ethernet/i825xx/eexpress.c1
-rw-r--r--drivers/net/ethernet/i825xx/ether1.c1
-rw-r--r--drivers/net/ethernet/i825xx/znet.c1
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.c7
-rw-r--r--drivers/net/ethernet/korina.c1
-rw-r--r--drivers/net/ethernet/marvell/mv643xx_eth.c1
-rw-r--r--drivers/net/ethernet/marvell/pxa168_eth.c1
-rw-r--r--drivers/net/ethernet/micrel/ks8842.c4
-rw-r--r--drivers/net/ethernet/natsemi/jazzsonic.c1
-rw-r--r--drivers/net/ethernet/natsemi/macsonic.c1
-rw-r--r--drivers/net/ethernet/natsemi/ns83820.c1
-rw-r--r--drivers/net/ethernet/neterion/s2io.c1
-rw-r--r--drivers/net/ethernet/nvidia/forcedeth.c1
-rw-r--r--drivers/net/ethernet/realtek/atp.c1
-rw-r--r--drivers/net/ethernet/realtek/r8169.c1
-rw-r--r--drivers/net/ethernet/seeq/ether3.c1
-rw-r--r--drivers/net/ethernet/seeq/seeq8005.c1
-rw-r--r--drivers/net/ethernet/sfc/mtd.c10
-rw-r--r--drivers/net/ethernet/smsc/smc91c92_cs.c1
-rw-r--r--drivers/net/ethernet/smsc/smc91x.c2
-rw-r--r--drivers/net/ethernet/sun/cassini.c1
-rw-r--r--drivers/net/ethernet/sun/sunbmac.c1
-rw-r--r--drivers/net/ethernet/sun/sungem.c1
-rw-r--r--drivers/net/ethernet/sun/sunhme.c1
-rw-r--r--drivers/net/ethernet/sun/sunqe.c1
-rw-r--r--drivers/net/ethernet/sun/sunvnet.c5
-rw-r--r--drivers/net/ethernet/tundra/tsi108_eth.c1
-rw-r--r--drivers/net/ethernet/xircom/xirc2ps_cs.c1
-rw-r--r--drivers/net/hamradio/6pack.c1
-rw-r--r--drivers/net/hamradio/baycom_par.c1
-rw-r--r--drivers/net/hamradio/bpqether.c1
-rw-r--r--drivers/net/hamradio/mkiss.c1
-rw-r--r--drivers/net/hamradio/scc.c1
-rw-r--r--drivers/net/hamradio/yam.c1
-rw-r--r--drivers/net/hippi/rrunner.c1
-rw-r--r--drivers/net/irda/Kconfig2
-rw-r--r--drivers/net/irda/donauboe.c1
-rw-r--r--drivers/net/irda/sa1100_ir.c953
-rw-r--r--drivers/net/loopback.c1
-rw-r--r--drivers/net/plip/plip.c1
-rw-r--r--drivers/net/slip/slhc.c1
-rw-r--r--drivers/net/slip/slip.c1
-rw-r--r--drivers/net/tokenring/3c359.c1
-rw-r--r--drivers/net/tokenring/abyss.c1
-rw-r--r--drivers/net/tokenring/ibmtr_cs.c1
-rw-r--r--drivers/net/tokenring/lanstreamer.c1
-rw-r--r--drivers/net/tokenring/madgemc.c1
-rw-r--r--drivers/net/tokenring/olympic.c1
-rw-r--r--drivers/net/tokenring/proteon.c1
-rw-r--r--drivers/net/tokenring/skisa.c1
-rw-r--r--drivers/net/tokenring/smctr.c1
-rw-r--r--drivers/net/tokenring/tms380tr.c1
-rw-r--r--drivers/net/tokenring/tmspci.c1
-rw-r--r--drivers/net/tun.c1
-rw-r--r--drivers/net/wan/Kconfig4
-rw-r--r--drivers/net/wan/dlci.c1
-rw-r--r--drivers/net/wan/dscc4.c1
-rw-r--r--drivers/net/wan/hd64570.c1
-rw-r--r--drivers/net/wan/hd64572.c1
-rw-r--r--drivers/net/wan/lapbether.c1
-rw-r--r--drivers/net/wan/sdla.c1
-rw-r--r--drivers/net/wan/x25_asy.c1
-rw-r--r--drivers/net/wireless/airo.c1
-rw-r--r--drivers/net/wireless/airo_cs.c1
-rw-r--r--drivers/net/wireless/atmel.c1
-rw-r--r--drivers/net/wireless/atmel_cs.c1
-rw-r--r--drivers/net/wireless/prism54/islpci_mgt.c1
-rw-r--r--drivers/net/wireless/ray_cs.c1
-rw-r--r--drivers/net/wireless/wl3501_cs.c1
-rw-r--r--drivers/nubus/nubus.c1
-rw-r--r--drivers/of/Kconfig4
-rw-r--r--drivers/of/Makefile1
-rw-r--r--drivers/of/gpio.c11
-rw-r--r--drivers/of/of_mtd.c85
-rw-r--r--drivers/parisc/dino.c1
-rw-r--r--drivers/parisc/iosapic.c1
-rw-r--r--drivers/parisc/lba_pci.c1
-rw-r--r--drivers/pci/pci-acpi.c40
-rw-r--r--drivers/pci/pcie/aspm.c13
-rw-r--r--drivers/pcmcia/at91_cf.c52
-rw-r--r--drivers/pcmcia/bcm63xx_pcmcia.c2
-rw-r--r--drivers/pcmcia/bfin_cf_pcmcia.c13
-rw-r--r--drivers/pcmcia/cs.c1
-rw-r--r--drivers/pcmcia/db1xxx_ss.c17
-rw-r--r--drivers/pcmcia/electra_cf.c12
-rw-r--r--drivers/pcmcia/i82092.c12
-rw-r--r--drivers/pcmcia/i82365.c1
-rw-r--r--drivers/pcmcia/m32r_cfc.c1
-rw-r--r--drivers/pcmcia/m32r_pcc.c1
-rw-r--r--drivers/pcmcia/m8xx_pcmcia.c14
-rw-r--r--drivers/pcmcia/pd6729.c10
-rw-r--r--drivers/pcmcia/pxa2xx_base.c1
-rw-r--r--drivers/pcmcia/pxa2xx_viper.c13
-rw-r--r--drivers/pcmcia/sa1111_generic.c55
-rw-r--r--drivers/pcmcia/sa1111_neponset.c7
-rw-r--r--drivers/pcmcia/sa11xx_base.c1
-rw-r--r--drivers/pcmcia/soc_common.c1
-rw-r--r--drivers/pcmcia/socket_sysfs.c1
-rw-r--r--drivers/pcmcia/tcic.c1
-rw-r--r--drivers/pcmcia/vrc4173_cardu.c7
-rw-r--r--drivers/pcmcia/xxs1500_ss.c14
-rw-r--r--drivers/pcmcia/yenta_socket.c2
-rw-r--r--drivers/platform/x86/Kconfig71
-rw-r--r--drivers/platform/x86/Makefile6
-rw-r--r--drivers/platform/x86/acer-wmi.c152
-rw-r--r--drivers/platform/x86/acerhdf.c19
-rw-r--r--drivers/platform/x86/amilo-rfkill.c5
-rw-r--r--drivers/platform/x86/apple-gmux.c244
-rw-r--r--drivers/platform/x86/asus-laptop.c273
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c12
-rw-r--r--drivers/platform/x86/asus-wmi.c68
-rw-r--r--drivers/platform/x86/asus-wmi.h14
-rw-r--r--drivers/platform/x86/asus_acpi.c1513
-rw-r--r--drivers/platform/x86/compal-laptop.c14
-rw-r--r--drivers/platform/x86/dell-laptop.c34
-rw-r--r--drivers/platform/x86/eeepc-laptop.c13
-rw-r--r--drivers/platform/x86/eeepc-wmi.c108
-rw-r--r--drivers/platform/x86/hdaps.c4
-rw-r--r--drivers/platform/x86/intel_ips.c13
-rw-r--r--drivers/platform/x86/intel_mid_powerbtn.c12
-rw-r--r--drivers/platform/x86/intel_mid_thermal.c14
-rw-r--r--drivers/platform/x86/intel_oaktrail.c2
-rw-r--r--drivers/platform/x86/samsung-laptop.c1749
-rw-r--r--drivers/platform/x86/sony-laptop.c15
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c2
-rw-r--r--drivers/platform/x86/toshiba_acpi.c254
-rw-r--r--drivers/platform/x86/xo1-rfkill.c13
-rw-r--r--drivers/pnp/pnpacpi/core.c7
-rw-r--r--drivers/pnp/pnpbios/bioscalls.c1
-rw-r--r--drivers/pnp/pnpbios/core.c1
-rw-r--r--drivers/power/Kconfig21
-rw-r--r--drivers/power/Makefile2
-rw-r--r--drivers/power/ab8500_btemp.c1124
-rw-r--r--drivers/power/ab8500_charger.c2789
-rw-r--r--drivers/power/ab8500_fg.c2637
-rw-r--r--drivers/power/abx500_chargalg.c1921
-rw-r--r--drivers/power/charger-manager.c67
-rw-r--r--drivers/power/da9052-battery.c15
-rw-r--r--drivers/power/ds2782_battery.c13
-rw-r--r--drivers/power/isp1704_charger.c1
-rw-r--r--drivers/power/lp8727_charger.c131
-rw-r--r--drivers/power/max17040_battery.c13
-rw-r--r--drivers/power/max17042_battery.c508
-rw-r--r--drivers/power/sbs-battery.c13
-rw-r--r--drivers/power/smb347-charger.c1294
-rw-r--r--drivers/power/z2_battery.c14
-rw-r--r--drivers/rtc/interface.c5
-rw-r--r--drivers/rtc/rtc-88pm860x.c27
-rw-r--r--drivers/rtc/rtc-mpc5121.c2
-rw-r--r--drivers/rtc/rtc-mv.c9
-rw-r--r--drivers/rtc/rtc-sa1100.c3
-rw-r--r--drivers/s390/char/sclp_cmd.c1
-rw-r--r--drivers/s390/cio/crw.c1
-rw-r--r--drivers/s390/cio/qdio_main.c1
-rw-r--r--drivers/s390/cio/qdio_setup.c3
-rw-r--r--drivers/s390/crypto/ap_bus.c2
-rw-r--r--drivers/sbus/char/flash.c1
-rw-r--r--drivers/sbus/char/openprom.c1
-rw-r--r--drivers/sbus/char/uctrl.c1
-rw-r--r--drivers/scsi/53c700.c1
-rw-r--r--drivers/scsi/BusLogic.c1
-rw-r--r--drivers/scsi/Kconfig1
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/advansys.c1
-rw-r--r--drivers/scsi/aha152x.c1
-rw-r--r--drivers/scsi/aha1542.c1
-rw-r--r--drivers/scsi/aha1740.c1
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.c8
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.c8
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c1
-rw-r--r--drivers/scsi/arm/acornscsi.c1
-rw-r--r--drivers/scsi/arm/arxescsi.c2
-rw-r--r--drivers/scsi/arm/cumana_1.c1
-rw-r--r--drivers/scsi/arm/fas216.c4
-rw-r--r--drivers/scsi/arm/fas216.h4
-rw-r--r--drivers/scsi/arm/oak.c1
-rw-r--r--drivers/scsi/atp870u.c5
-rw-r--r--drivers/scsi/bfa/bfa.h9
-rw-r--r--drivers/scsi/bfa/bfa_core.c693
-rw-r--r--drivers/scsi/bfa/bfa_defs_svc.h2
-rw-r--r--drivers/scsi/bfa/bfa_fcs_lport.c2
-rw-r--r--drivers/scsi/bfa/bfa_fcs_rport.c5
-rw-r--r--drivers/scsi/bfa/bfa_ioc.c188
-rw-r--r--drivers/scsi/bfa/bfa_ioc.h17
-rw-r--r--drivers/scsi/bfa/bfa_ioc_ct.c151
-rw-r--r--drivers/scsi/bfa/bfa_svc.c69
-rw-r--r--drivers/scsi/bfa/bfa_svc.h4
-rw-r--r--drivers/scsi/bfa/bfad_attr.c47
-rw-r--r--drivers/scsi/bfa/bfad_bsg.c62
-rw-r--r--drivers/scsi/bfa/bfad_bsg.h2
-rw-r--r--drivers/scsi/bfa/bfad_drv.h2
-rw-r--r--drivers/scsi/bfa/bfi_ms.h17
-rw-r--r--drivers/scsi/bfa/bfi_reg.h6
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c4
-rw-r--r--drivers/scsi/dtc.c1
-rw-r--r--drivers/scsi/fcoe/fcoe.c83
-rw-r--r--drivers/scsi/fcoe/fcoe_ctlr.c38
-rw-r--r--drivers/scsi/fd_mcs.c1
-rw-r--r--drivers/scsi/fdomain.c1
-rw-r--r--drivers/scsi/g_NCR5380.c1
-rw-r--r--drivers/scsi/gdth.c1
-rw-r--r--drivers/scsi/ibmmca.c1
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c7
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c7
-rw-r--r--drivers/scsi/ibmvscsi/ibmvstgt.c5
-rw-r--r--drivers/scsi/in2000.c1
-rw-r--r--drivers/scsi/ipr.c73
-rw-r--r--drivers/scsi/ipr.h16
-rw-r--r--drivers/scsi/libfc/fc_exch.c14
-rw-r--r--drivers/scsi/libfc/fc_lport.c10
-rw-r--r--drivers/scsi/lpfc/Makefile4
-rw-r--r--drivers/scsi/lpfc/lpfc.h8
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c4
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c80
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c17
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c24
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h8
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c41
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c10
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c488
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h13
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c62
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h4
-rw-r--r--drivers/scsi/mac53c94.c1
-rw-r--r--drivers/scsi/mac_scsi.c1
-rw-r--r--drivers/scsi/mesh.c1
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c6
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.c4
-rw-r--r--drivers/scsi/ncr53c8xx.c1
-rw-r--r--drivers/scsi/nsp32.c1
-rw-r--r--drivers/scsi/osst.c1
-rw-r--r--drivers/scsi/pas16.c1
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.c18
-rw-r--r--drivers/scsi/qla1280.c1
-rw-r--r--drivers/scsi/qla4xxx/ql4_isr.c4
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c10
-rw-r--r--drivers/scsi/qla4xxx/ql4_version.h2
-rw-r--r--drivers/scsi/qlogicpti.c1
-rw-r--r--drivers/scsi/scsi_debug.c27
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c8
-rw-r--r--drivers/scsi/sd.c15
-rw-r--r--drivers/scsi/st.c22
-rw-r--r--drivers/scsi/st.h1
-rw-r--r--drivers/scsi/sun3_scsi.c1
-rw-r--r--drivers/scsi/sun3_scsi_vme.c1
-rw-r--r--drivers/scsi/sym53c416.c1
-rw-r--r--drivers/scsi/t128.c1
-rw-r--r--drivers/scsi/u14-34f.c1
-rw-r--r--drivers/scsi/ufs/Kconfig49
-rw-r--r--drivers/scsi/ufs/Makefile2
-rw-r--r--drivers/scsi/ufs/ufs.h207
-rw-r--r--drivers/scsi/ufs/ufshcd.c1978
-rw-r--r--drivers/scsi/ufs/ufshci.h376
-rw-r--r--drivers/scsi/ultrastor.c1
-rw-r--r--drivers/scsi/vmw_pvscsi.c65
-rw-r--r--drivers/scsi/vmw_pvscsi.h109
-rw-r--r--drivers/scsi/wd7000.c1
-rw-r--r--drivers/sh/intc/balancing.c2
-rw-r--r--drivers/sh/intc/chip.c37
-rw-r--r--drivers/sh/intc/core.c13
-rw-r--r--drivers/sh/intc/handle.c7
-rw-r--r--drivers/sh/intc/internals.h9
-rw-r--r--drivers/sh/intc/virq.c2
-rw-r--r--drivers/spi/spi-dw-mid.c7
-rw-r--r--drivers/spi/spi-ep93xx.c4
-rw-r--r--drivers/spi/spi-omap-uwire.c1
-rw-r--r--drivers/spi/spi-orion.c5
-rw-r--r--drivers/spi/spi-pl022.c6
-rw-r--r--drivers/spi/spi-topcliff-pch.c4
-rw-r--r--drivers/staging/android/binder.c13
-rw-r--r--drivers/staging/asus_oled/README2
-rw-r--r--drivers/staging/comedi/drivers.c1
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidas64.c1
-rw-r--r--drivers/staging/comedi/drivers/mite.c1
-rw-r--r--drivers/staging/crystalhd/bc_dts_defs.h2
-rw-r--r--drivers/staging/crystalhd/crystalhd.h1
-rw-r--r--drivers/staging/crystalhd/crystalhd_lnx.h1
-rw-r--r--drivers/staging/crystalhd/crystalhd_misc.h1
-rw-r--r--drivers/staging/et131x/et131x.c1
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c1
-rw-r--r--drivers/staging/media/go7007/go7007-driver.c1
-rw-r--r--drivers/staging/media/go7007/go7007-i2c.c1
-rw-r--r--drivers/staging/media/go7007/go7007-v4l2.c1
-rw-r--r--drivers/staging/media/go7007/snd-go7007.c1
-rw-r--r--drivers/staging/media/lirc/lirc_serial.c1
-rw-r--r--drivers/staging/media/lirc/lirc_sir.c1
-rw-r--r--drivers/staging/mei/wd.c1
-rw-r--r--drivers/staging/panel/panel.c1
-rw-r--r--drivers/staging/sbe-2t3e3/io.c1
-rw-r--r--drivers/staging/ste_rmi4/Makefile2
-rw-r--r--drivers/staging/telephony/phonedev.c1
-rw-r--r--drivers/staging/tidspbridge/include/dspbridge/host_os.h1
-rw-r--r--drivers/staging/wlags49_h2/wl_cs.c1
-rw-r--r--drivers/staging/wlags49_h2/wl_main.c3
-rw-r--r--drivers/staging/wlags49_h2/wl_netdev.c3
-rw-r--r--drivers/staging/wlags49_h2/wl_pci.c1
-rw-r--r--drivers/staging/wlags49_h2/wl_util.c3
-rw-r--r--drivers/thermal/Kconfig8
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/spear_thermal.c206
-rw-r--r--drivers/thermal/thermal_sys.c94
-rw-r--r--drivers/tty/amiserial.c1
-rw-r--r--drivers/tty/hvc/hvc_vio.c7
-rw-r--r--drivers/tty/hvc/hvcs.c5
-rw-r--r--drivers/tty/isicom.c3
-rw-r--r--drivers/tty/moxa.c1
-rw-r--r--drivers/tty/mxser.c1
-rw-r--r--drivers/tty/n_hdlc.c1
-rw-r--r--drivers/tty/n_tty.c1
-rw-r--r--drivers/tty/pty.c1
-rw-r--r--drivers/tty/serial/21285.c1
-rw-r--r--drivers/tty/serial/68328serial.c1
-rw-r--r--drivers/tty/serial/8250/serial_cs.c1
-rw-r--r--drivers/tty/serial/amba-pl011.c9
-rw-r--r--drivers/tty/serial/crisv10.c2
-rw-r--r--drivers/tty/serial/dz.c1
-rw-r--r--drivers/tty/serial/icom.c1
-rw-r--r--drivers/tty/serial/msm_serial_hs.c1
-rw-r--r--drivers/tty/serial/pch_uart.c4
-rw-r--r--drivers/tty/serial/sa1100.c1
-rw-r--r--drivers/tty/serial/sh-sci.c19
-rw-r--r--drivers/tty/serial/sunhv.c1
-rw-r--r--drivers/tty/serial/sunsab.c1
-rw-r--r--drivers/tty/serial/sunsu.c1
-rw-r--r--drivers/tty/serial/sunzilog.c1
-rw-r--r--drivers/tty/serial/zs.c1
-rw-r--r--drivers/tty/synclink.c1
-rw-r--r--drivers/tty/synclink_gt.c1
-rw-r--r--drivers/tty/synclinkmp.c1
-rw-r--r--drivers/tty/tty_io.c1
-rw-r--r--drivers/tty/tty_ioctl.c1
-rw-r--r--drivers/tty/vt/vt.c1
-rw-r--r--drivers/usb/Kconfig2
-rw-r--r--drivers/usb/gadget/Kconfig2
-rw-r--r--drivers/usb/gadget/amd5536udc.c1
-rw-r--r--drivers/usb/gadget/at91_udc.c41
-rw-r--r--drivers/usb/gadget/dummy_hcd.c1
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c1
-rw-r--r--drivers/usb/gadget/goku_udc.c1
-rw-r--r--drivers/usb/gadget/langwell_udc.c1
-rw-r--r--drivers/usb/gadget/mv_udc_core.c1
-rw-r--r--drivers/usb/gadget/net2272.c1
-rw-r--r--drivers/usb/gadget/net2280.c1
-rw-r--r--drivers/usb/gadget/omap_udc.c1
-rw-r--r--drivers/usb/gadget/printer.c1
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c1
-rw-r--r--drivers/usb/gadget/rndis.c1
-rw-r--r--drivers/usb/gadget/s3c2410_udc.c1
-rw-r--r--drivers/usb/host/ehci-atmel.c24
-rw-r--r--drivers/usb/host/ehci-hcd.c1
-rw-r--r--drivers/usb/host/isp116x-hcd.c1
-rw-r--r--drivers/usb/host/isp1362-hcd.c1
-rw-r--r--drivers/usb/host/ohci-at91.c101
-rw-r--r--drivers/usb/host/ohci-hcd.c1
-rw-r--r--drivers/usb/host/ohci-sa1111.c297
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c1
-rw-r--r--drivers/usb/host/sl811-hcd.c1
-rw-r--r--drivers/usb/host/u132-hcd.c1
-rw-r--r--drivers/usb/host/uhci-hcd.c1
-rw-r--r--drivers/usb/musb/ux500_dma.c4
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c5
-rw-r--r--drivers/usb/serial/ftdi_sio.c3
-rw-r--r--drivers/usb/storage/Kconfig2
-rw-r--r--drivers/video/amifb.c1
-rw-r--r--drivers/video/backlight/88pm860x_bl.c49
-rw-r--r--drivers/video/backlight/apple_bl.c23
-rw-r--r--drivers/video/backlight/tosa_lcd.c2
-rw-r--r--drivers/video/bt431.h1
-rw-r--r--drivers/video/bt455.h1
-rw-r--r--drivers/video/console/fbcon.c1
-rw-r--r--drivers/video/console/newport_con.c1
-rw-r--r--drivers/video/cyber2000fb.c1
-rw-r--r--drivers/video/dnfb.c1
-rw-r--r--drivers/video/mx3fb.c4
-rw-r--r--drivers/video/neofb.c1
-rw-r--r--drivers/video/omap2/vrfb.c1
-rw-r--r--drivers/video/pmag-ba-fb.c1
-rw-r--r--drivers/video/pmagb-b-fb.c1
-rw-r--r--drivers/video/q40fb.c1
-rw-r--r--drivers/video/sa1100fb.c493
-rw-r--r--drivers/video/sa1100fb.h76
-rw-r--r--drivers/video/savage/savagefb_driver.c1
-rw-r--r--drivers/virtio/config.c1
-rw-r--r--drivers/virtio/virtio_balloon.c14
-rw-r--r--drivers/virtio/virtio_pci.c74
-rw-r--r--drivers/watchdog/Kconfig8
-rw-r--r--drivers/watchdog/acquirewdt.c28
-rw-r--r--drivers/watchdog/advantechwdt.c35
-rw-r--r--drivers/watchdog/alim1535_wdt.c25
-rw-r--r--drivers/watchdog/alim7101_wdt.c56
-rw-r--r--drivers/watchdog/ar7_wdt.c38
-rw-r--r--drivers/watchdog/at32ap700x_wdt.c4
-rw-r--r--drivers/watchdog/at91rm9200_wdt.c14
-rw-r--r--drivers/watchdog/at91sam9_wdt.c12
-rw-r--r--drivers/watchdog/ath79_wdt.c9
-rw-r--r--drivers/watchdog/bcm47xx_wdt.c18
-rw-r--r--drivers/watchdog/bcm63xx_wdt.c11
-rw-r--r--drivers/watchdog/bfin_wdt.c31
-rw-r--r--drivers/watchdog/booke_wdt.c15
-rw-r--r--drivers/watchdog/coh901327_wdt.c206
-rw-r--r--drivers/watchdog/cpu5wdt.c19
-rw-r--r--drivers/watchdog/cpwd.c20
-rw-r--r--drivers/watchdog/dw_wdt.c7
-rw-r--r--drivers/watchdog/ep93xx_wdt.c220
-rw-r--r--drivers/watchdog/eurotechwdt.c33
-rw-r--r--drivers/watchdog/f71808e_wdt.c40
-rw-r--r--drivers/watchdog/gef_wdt.c15
-rw-r--r--drivers/watchdog/geodewdt.c9
-rw-r--r--drivers/watchdog/hpwdt.c31
-rw-r--r--drivers/watchdog/i6300esb.c39
-rw-r--r--drivers/watchdog/iTCO_vendor_support.c7
-rw-r--r--drivers/watchdog/iTCO_wdt.c59
-rw-r--r--drivers/watchdog/ib700wdt.c27
-rw-r--r--drivers/watchdog/ibmasr.c18
-rw-r--r--drivers/watchdog/imx2_wdt.c14
-rw-r--r--drivers/watchdog/indydog.c24
-rw-r--r--drivers/watchdog/intel_scu_watchdog.c71
-rw-r--r--drivers/watchdog/intel_scu_watchdog.h1
-rw-r--r--drivers/watchdog/iop_wdt.c15
-rw-r--r--drivers/watchdog/it8712f_wdt.c39
-rw-r--r--drivers/watchdog/it87_wdt.c47
-rw-r--r--drivers/watchdog/ixp2000_wdt.c11
-rw-r--r--drivers/watchdog/ixp4xx_wdt.c15
-rw-r--r--drivers/watchdog/jz4740_wdt.c265
-rw-r--r--drivers/watchdog/ks8695_wdt.c10
-rw-r--r--drivers/watchdog/lantiq_wdt.c8
-rw-r--r--drivers/watchdog/m54xx_wdt.c14
-rw-r--r--drivers/watchdog/machzwd.c38
-rw-r--r--drivers/watchdog/max63xx_wdt.c194
-rw-r--r--drivers/watchdog/mixcomwd.c29
-rw-r--r--drivers/watchdog/mpc8xxx_wdt.c17
-rw-r--r--drivers/watchdog/mpcore_wdt.c111
-rw-r--r--drivers/watchdog/mv64x60_wdt.c15
-rw-r--r--drivers/watchdog/nuc900_wdt.c4
-rw-r--r--drivers/watchdog/nv_tco.c46
-rw-r--r--drivers/watchdog/octeon-wdt-main.c16
-rw-r--r--drivers/watchdog/of_xilinx_wdt.c41
-rw-r--r--drivers/watchdog/omap_wdt.c4
-rw-r--r--drivers/watchdog/orion_wdt.c39
-rw-r--r--drivers/watchdog/pc87413_wdt.c58
-rw-r--r--drivers/watchdog/pcwd.c127
-rw-r--r--drivers/watchdog/pcwd_pci.c113
-rw-r--r--drivers/watchdog/pcwd_usb.c78
-rw-r--r--drivers/watchdog/pika_wdt.c23
-rw-r--r--drivers/watchdog/pnx4008_wdt.c263
-rw-r--r--drivers/watchdog/pnx833x_wdt.c30
-rw-r--r--drivers/watchdog/rc32434_wdt.c33
-rw-r--r--drivers/watchdog/riowd.c10
-rw-r--r--drivers/watchdog/s3c2410_wdt.c31
-rw-r--r--drivers/watchdog/sa1100_wdt.c11
-rw-r--r--drivers/watchdog/sb_wdog.c28
-rw-r--r--drivers/watchdog/sbc60xxwdt.c42
-rw-r--r--drivers/watchdog/sbc7240_wdt.c45
-rw-r--r--drivers/watchdog/sbc8360.c25
-rw-r--r--drivers/watchdog/sbc_epx_c3.c23
-rw-r--r--drivers/watchdog/sbc_fitpc2_wdt.c14
-rw-r--r--drivers/watchdog/sc1200wdt.c34
-rw-r--r--drivers/watchdog/sc520_wdt.c40
-rw-r--r--drivers/watchdog/sch311x_wdt.c17
-rw-r--r--drivers/watchdog/scx200_wdt.c25
-rw-r--r--drivers/watchdog/shwdt.c19
-rw-r--r--drivers/watchdog/smsc37b787_wdt.c43
-rw-r--r--drivers/watchdog/softdog.c211
-rw-r--r--drivers/watchdog/sp5100_tco.c35
-rw-r--r--drivers/watchdog/sp805_wdt.c111
-rw-r--r--drivers/watchdog/stmp3xxx_wdt.c8
-rw-r--r--drivers/watchdog/ts72xx_wdt.c4
-rw-r--r--drivers/watchdog/twl4030_wdt.c4
-rw-r--r--drivers/watchdog/txx9wdt.c184
-rw-r--r--drivers/watchdog/via_wdt.c13
-rw-r--r--drivers/watchdog/w83627hf_wdt.c39
-rw-r--r--drivers/watchdog/w83697hf_wdt.c44
-rw-r--r--drivers/watchdog/w83697ug_wdt.c39
-rw-r--r--drivers/watchdog/w83877f_wdt.c41
-rw-r--r--drivers/watchdog/w83977f_wdt.c39
-rw-r--r--drivers/watchdog/wafer5823wdt.c34
-rw-r--r--drivers/watchdog/watchdog_core.c4
-rw-r--r--drivers/watchdog/watchdog_dev.c16
-rw-r--r--drivers/watchdog/wdrtas.c71
-rw-r--r--drivers/watchdog/wdt.c56
-rw-r--r--drivers/watchdog/wdt285.c13
-rw-r--r--drivers/watchdog/wdt977.c41
-rw-r--r--drivers/watchdog/wdt_pci.c71
-rw-r--r--drivers/watchdog/wm831x_wdt.c6
-rw-r--r--drivers/watchdog/wm8350_wdt.c223
-rw-r--r--drivers/watchdog/xen_wdt.c42
1036 files changed, 46122 insertions, 15568 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 6f0459c..d236aef 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -50,6 +50,8 @@ source "drivers/i2c/Kconfig"
source "drivers/spi/Kconfig"
+source "drivers/hsi/Kconfig"
+
source "drivers/pps/Kconfig"
source "drivers/ptp/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 262b19d..95952c8 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_ATA) += ata/
obj-$(CONFIG_TARGET_CORE) += target/
obj-$(CONFIG_MTD) += mtd/
obj-$(CONFIG_SPI) += spi/
+obj-y += hsi/
obj-y += net/
obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 7556913..47768ff 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -384,6 +384,15 @@ config ACPI_CUSTOM_METHOD
load additional kernel modules after boot, this feature may be used
to override that restriction).
+config ACPI_BGRT
+ tristate "Boottime Graphics Resource Table support"
+ default n
+ help
+ This driver adds support for exposing the ACPI Boottime Graphics
+ Resource Table, which allows the operating system to obtain
+ data from the firmware boot splash. It will appear under
+ /sys/firmware/acpi/bgrt/ .
+
source "drivers/acpi/apei/Kconfig"
endif # ACPI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 1567028..47199e2 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_ACPI_SBS) += sbs.o
obj-$(CONFIG_ACPI_HED) += hed.o
obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
+obj-$(CONFIG_ACPI_BGRT) += bgrt.o
# processor has its own "processor." module_param namespace
processor-y := processor_driver.o processor_throttling.o
diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile
index 0ca208b..793b8cc 100644
--- a/drivers/acpi/acpica/Makefile
+++ b/drivers/acpi/acpica/Makefile
@@ -68,12 +68,14 @@ acpi-y += \
acpi-y += \
hwacpi.o \
+ hwesleep.o \
hwgpe.o \
hwpci.o \
hwregs.o \
hwsleep.o \
hwvalid.o \
- hwxface.o
+ hwxface.o \
+ hwxfsleep.o
acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o
diff --git a/drivers/acpi/acpica/accommon.h b/drivers/acpi/acpica/accommon.h
index a44bd42..8a7d51b 100644
--- a/drivers/acpi/acpica/accommon.h
+++ b/drivers/acpi/acpica/accommon.h
@@ -51,7 +51,6 @@
*
* Note: The order of these include files is important.
*/
-#include "acconfig.h" /* Global configuration constants */
#include "acmacros.h" /* C macros */
#include "aclocal.h" /* Internal data types */
#include "acobject.h" /* ACPI internal object */
diff --git a/drivers/acpi/acpica/acconfig.h b/drivers/acpi/acpica/acconfig.h
deleted file mode 100644
index 1f30af6..0000000
--- a/drivers/acpi/acpica/acconfig.h
+++ /dev/null
@@ -1,231 +0,0 @@
-/******************************************************************************
- *
- * Name: acconfig.h - Global configuration constants
- *
- *****************************************************************************/
-
-/*
- * Copyright (C) 2000 - 2012, Intel Corp.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- * substantially similar to the "NO WARRANTY" disclaimer below
- * ("Disclaimer") and any redistribution must be conditioned upon
- * including a substantially similar Disclaimer requirement for further
- * binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- * of any contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-#ifndef _ACCONFIG_H
-#define _ACCONFIG_H
-
-/******************************************************************************
- *
- * Configuration options
- *
- *****************************************************************************/
-
-/*
- * ACPI_DEBUG_OUTPUT - This switch enables all the debug facilities of the
- * ACPI subsystem. This includes the DEBUG_PRINT output
- * statements. When disabled, all DEBUG_PRINT
- * statements are compiled out.
- *
- * ACPI_APPLICATION - Use this switch if the subsystem is going to be run
- * at the application level.
- *
- */
-
-/*
- * OS name, used for the _OS object. The _OS object is essentially obsolete,
- * but there is a large base of ASL/AML code in existing machines that check
- * for the string below. The use of this string usually guarantees that
- * the ASL will execute down the most tested code path. Also, there is some
- * code that will not execute the _OSI method unless _OS matches the string
- * below. Therefore, change this string at your own risk.
- */
-#define ACPI_OS_NAME "Microsoft Windows NT"
-
-/* Maximum objects in the various object caches */
-
-#define ACPI_MAX_STATE_CACHE_DEPTH 96 /* State objects */
-#define ACPI_MAX_PARSE_CACHE_DEPTH 96 /* Parse tree objects */
-#define ACPI_MAX_EXTPARSE_CACHE_DEPTH 96 /* Parse tree objects */
-#define ACPI_MAX_OBJECT_CACHE_DEPTH 96 /* Interpreter operand objects */
-#define ACPI_MAX_NAMESPACE_CACHE_DEPTH 96 /* Namespace objects */
-
-/*
- * Should the subsystem abort the loading of an ACPI table if the
- * table checksum is incorrect?
- */
-#define ACPI_CHECKSUM_ABORT FALSE
-
-/******************************************************************************
- *
- * Subsystem Constants
- *
- *****************************************************************************/
-
-/* Version of ACPI supported */
-
-#define ACPI_CA_SUPPORT_LEVEL 3
-
-/* Maximum count for a semaphore object */
-
-#define ACPI_MAX_SEMAPHORE_COUNT 256
-
-/* Maximum object reference count (detects object deletion issues) */
-
-#define ACPI_MAX_REFERENCE_COUNT 0x1000
-
-/* Default page size for use in mapping memory for operation regions */
-
-#define ACPI_DEFAULT_PAGE_SIZE 4096 /* Must be power of 2 */
-
-/* owner_id tracking. 8 entries allows for 255 owner_ids */
-
-#define ACPI_NUM_OWNERID_MASKS 8
-
-/* Size of the root table array is increased by this increment */
-
-#define ACPI_ROOT_TABLE_SIZE_INCREMENT 4
-
-/* Maximum number of While() loop iterations before forced abort */
-
-#define ACPI_MAX_LOOP_ITERATIONS 0xFFFF
-
-/* Maximum sleep allowed via Sleep() operator */
-
-#define ACPI_MAX_SLEEP 2000 /* Two seconds */
-
-/* Address Range lists are per-space_id (Memory and I/O only) */
-
-#define ACPI_ADDRESS_RANGE_MAX 2
-
-/******************************************************************************
- *
- * ACPI Specification constants (Do not change unless the specification changes)
- *
- *****************************************************************************/
-
-/* Number of distinct GPE register blocks and register width */
-
-#define ACPI_MAX_GPE_BLOCKS 2
-#define ACPI_GPE_REGISTER_WIDTH 8
-
-/* Method info (in WALK_STATE), containing local variables and argumetns */
-
-#define ACPI_METHOD_NUM_LOCALS 8
-#define ACPI_METHOD_MAX_LOCAL 7
-
-#define ACPI_METHOD_NUM_ARGS 7
-#define ACPI_METHOD_MAX_ARG 6
-
-/* Length of _HID, _UID, _CID, and UUID values */
-
-#define ACPI_DEVICE_ID_LENGTH 0x09
-#define ACPI_MAX_CID_LENGTH 48
-#define ACPI_UUID_LENGTH 16
-
-/*
- * Operand Stack (in WALK_STATE), Must be large enough to contain METHOD_MAX_ARG
- */
-#define ACPI_OBJ_NUM_OPERANDS 8
-#define ACPI_OBJ_MAX_OPERAND 7
-
-/* Number of elements in the Result Stack frame, can be an arbitrary value */
-
-#define ACPI_RESULTS_FRAME_OBJ_NUM 8
-
-/*
- * Maximal number of elements the Result Stack can contain,
- * it may be an arbitray value not exceeding the types of
- * result_size and result_count (now u8).
- */
-#define ACPI_RESULTS_OBJ_NUM_MAX 255
-
-/* Names within the namespace are 4 bytes long */
-
-#define ACPI_NAME_SIZE 4
-#define ACPI_PATH_SEGMENT_LENGTH 5 /* 4 chars for name + 1 char for separator */
-#define ACPI_PATH_SEPARATOR '.'
-
-/* Sizes for ACPI table headers */
-
-#define ACPI_OEM_ID_SIZE 6
-#define ACPI_OEM_TABLE_ID_SIZE 8
-
-/* Constants used in searching for the RSDP in low memory */
-
-#define ACPI_EBDA_PTR_LOCATION 0x0000040E /* Physical Address */
-#define ACPI_EBDA_PTR_LENGTH 2
-#define ACPI_EBDA_WINDOW_SIZE 1024
-#define ACPI_HI_RSDP_WINDOW_BASE 0x000E0000 /* Physical Address */
-#define ACPI_HI_RSDP_WINDOW_SIZE 0x00020000
-#define ACPI_RSDP_SCAN_STEP 16
-
-/* Operation regions */
-
-#define ACPI_USER_REGION_BEGIN 0x80
-
-/* Maximum space_ids for Operation Regions */
-
-#define ACPI_MAX_ADDRESS_SPACE 255
-
-/* Array sizes. Used for range checking also */
-
-#define ACPI_MAX_MATCH_OPCODE 5
-
-/* RSDP checksums */
-
-#define ACPI_RSDP_CHECKSUM_LENGTH 20
-#define ACPI_RSDP_XCHECKSUM_LENGTH 36
-
-/* SMBus, GSBus and IPMI bidirectional buffer size */
-
-#define ACPI_SMBUS_BUFFER_SIZE 34
-#define ACPI_GSBUS_BUFFER_SIZE 34
-#define ACPI_IPMI_BUFFER_SIZE 66
-
-/* _sx_d and _sx_w control methods */
-
-#define ACPI_NUM_sx_d_METHODS 4
-#define ACPI_NUM_sx_w_METHODS 5
-
-/******************************************************************************
- *
- * ACPI AML Debugger
- *
- *****************************************************************************/
-
-#define ACPI_DEBUGGER_MAX_ARGS 8 /* Must be max method args + 1 */
-
-#define ACPI_DEBUGGER_COMMAND_PROMPT '-'
-#define ACPI_DEBUGGER_EXECUTE_PROMPT '%'
-
-#endif /* _ACCONFIG_H */
diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index deaa819..5e8abb0 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -111,7 +111,7 @@ acpi_status acpi_db_find_name_in_namespace(char *name_arg);
void acpi_db_set_scope(char *name);
-acpi_status acpi_db_sleep(char *object_arg);
+ACPI_HW_DEPENDENT_RETURN_OK(acpi_status acpi_db_sleep(char *object_arg))
void acpi_db_find_references(char *object_arg);
@@ -119,11 +119,13 @@ void acpi_db_display_locks(void);
void acpi_db_display_resources(char *object_arg);
-void acpi_db_display_gpes(void);
+ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_db_display_gpes(void))
void acpi_db_check_integrity(void);
-void acpi_db_generate_gpe(char *gpe_arg, char *block_arg);
+ACPI_HW_DEPENDENT_RETURN_VOID(void
+ acpi_db_generate_gpe(char *gpe_arg,
+ char *block_arg))
void acpi_db_check_predefined_names(void);
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index c53caa5..d700f63 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -69,11 +69,10 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node *node,
*/
acpi_status acpi_ev_init_global_lock_handler(void);
-acpi_status acpi_ev_acquire_global_lock(u16 timeout);
-
-acpi_status acpi_ev_release_global_lock(void);
-
-acpi_status acpi_ev_remove_global_lock_handler(void);
+ACPI_HW_DEPENDENT_RETURN_OK(acpi_status
+ acpi_ev_acquire_global_lock(u16 timeout))
+ ACPI_HW_DEPENDENT_RETURN_OK(acpi_status acpi_ev_release_global_lock(void))
+ acpi_status acpi_ev_remove_global_lock_handler(void);
/*
* evgpe - Low-level GPE support
@@ -114,7 +113,9 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
struct acpi_gpe_block_info *gpe_block,
void *context);
-acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block);
+ACPI_HW_DEPENDENT_RETURN_OK(acpi_status
+ acpi_ev_delete_gpe_block(struct acpi_gpe_block_info
+ *gpe_block))
u32
acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
@@ -126,9 +127,10 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
*/
acpi_status acpi_ev_gpe_initialize(void);
-void acpi_ev_update_gpes(acpi_owner_id table_owner_id);
+ACPI_HW_DEPENDENT_RETURN_VOID(void
+ acpi_ev_update_gpes(acpi_owner_id table_owner_id))
-acpi_status
+ acpi_status
acpi_ev_match_gpe_method(acpi_handle obj_handle,
u32 level, void *context, void **return_value);
@@ -237,6 +239,5 @@ acpi_status acpi_ev_remove_sci_handler(void);
u32 acpi_ev_initialize_sCI(u32 program_sCI);
-void acpi_ev_terminate(void);
-
+ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_ev_terminate(void))
#endif /* __ACEVENTS_H__ */
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index 2853f76..4f7d3f5 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -147,7 +147,7 @@ u8 acpi_gbl_system_awake_and_running;
*/
u8 acpi_gbl_reduced_hardware;
-#endif
+#endif /* DEFINE_ACPI_GLOBALS */
/* Do not disassemble buffers to resource descriptors */
@@ -184,8 +184,12 @@ ACPI_EXTERN u32 acpi_gbl_trace_dbg_layer;
* found in the RSDT/XSDT.
*/
ACPI_EXTERN struct acpi_table_list acpi_gbl_root_table_list;
+
+#if (!ACPI_REDUCED_HARDWARE)
ACPI_EXTERN struct acpi_table_facs *acpi_gbl_FACS;
+#endif /* !ACPI_REDUCED_HARDWARE */
+
/* These addresses are calculated from the FADT Event Block addresses */
ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1a_status;
@@ -397,10 +401,15 @@ ACPI_EXTERN struct acpi_fixed_event_handler
ACPI_EXTERN struct acpi_gpe_xrupt_info *acpi_gbl_gpe_xrupt_list_head;
ACPI_EXTERN struct acpi_gpe_block_info
*acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS];
+
+#if (!ACPI_REDUCED_HARDWARE)
+
ACPI_EXTERN u8 acpi_gbl_all_gpes_initialized;
ACPI_EXTERN ACPI_GBL_EVENT_HANDLER acpi_gbl_global_event_handler;
ACPI_EXTERN void *acpi_gbl_global_event_handler_context;
+#endif /* !ACPI_REDUCED_HARDWARE */
+
/*****************************************************************************
*
* Debugger globals
diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h
index 677793e..5ccb99a 100644
--- a/drivers/acpi/acpica/achware.h
+++ b/drivers/acpi/acpica/achware.h
@@ -81,6 +81,26 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value);
acpi_status acpi_hw_clear_acpi_status(void);
/*
+ * hwsleep - sleep/wake support (Legacy sleep registers)
+ */
+acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags);
+
+acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags);
+
+acpi_status acpi_hw_legacy_wake(u8 sleep_state, u8 flags);
+
+/*
+ * hwesleep - sleep/wake support (Extended FADT-V5 sleep registers)
+ */
+void acpi_hw_execute_sleep_method(char *method_name, u32 integer_argument);
+
+acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags);
+
+acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags);
+
+acpi_status acpi_hw_extended_wake(u8 sleep_state, u8 flags);
+
+/*
* hwvalid - Port I/O with validation
*/
acpi_status acpi_hw_read_port(acpi_io_address address, u32 *value, u32 width);
@@ -128,16 +148,4 @@ acpi_status
acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
acpi_handle root_pci_device, acpi_handle pci_region);
-#ifdef ACPI_FUTURE_USAGE
-/*
- * hwtimer - ACPI Timer prototypes
- */
-acpi_status acpi_get_timer_resolution(u32 * resolution);
-
-acpi_status acpi_get_timer(u32 * ticks);
-
-acpi_status
-acpi_get_timer_duration(u32 start_ticks, u32 end_ticks, u32 * time_elapsed);
-#endif /* ACPI_FUTURE_USAGE */
-
#endif /* __ACHWARE_H__ */
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index 3f24068..e3922ca 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -370,6 +370,7 @@ struct acpi_predefined_data {
/* Defines for Flags field above */
#define ACPI_OBJECT_REPAIRED 1
+#define ACPI_OBJECT_WRAPPED 2
/*
* Bitmapped return value types
diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h
index ef338a9..f119f47 100644
--- a/drivers/acpi/acpica/acmacros.h
+++ b/drivers/acpi/acpica/acmacros.h
@@ -516,6 +516,12 @@
#endif /* ACPI_DEBUG_OUTPUT */
+#if (!ACPI_REDUCED_HARDWARE)
+#define ACPI_HW_OPTIONAL_FUNCTION(addr) addr
+#else
+#define ACPI_HW_OPTIONAL_FUNCTION(addr) NULL
+#endif
+
/*
* Some code only gets executed when the debugger is built in.
* Note that this is entirely independent of whether the
diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h
index 2c9e0f0..9b19d4b 100644
--- a/drivers/acpi/acpica/acnamesp.h
+++ b/drivers/acpi/acpica/acnamesp.h
@@ -283,8 +283,9 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
union acpi_operand_object **return_object_ptr);
acpi_status
-acpi_ns_repair_package_list(struct acpi_predefined_data *data,
- union acpi_operand_object **obj_desc_ptr);
+acpi_ns_wrap_with_package(struct acpi_predefined_data *data,
+ union acpi_operand_object *original_object,
+ union acpi_operand_object **obj_desc_ptr);
acpi_status
acpi_ns_repair_null_element(struct acpi_predefined_data *data,
diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h
index d5bec30..6712965 100644
--- a/drivers/acpi/acpica/actables.h
+++ b/drivers/acpi/acpica/actables.h
@@ -67,6 +67,11 @@ acpi_status acpi_tb_resize_root_table_list(void);
acpi_status acpi_tb_verify_table(struct acpi_table_desc *table_desc);
+struct acpi_table_header *acpi_tb_table_override(struct acpi_table_header
+ *table_header,
+ struct acpi_table_desc
+ *table_desc);
+
acpi_status
acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index);
diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c
index 6729ebe..07e4dc4 100644
--- a/drivers/acpi/acpica/evevent.c
+++ b/drivers/acpi/acpica/evevent.c
@@ -47,7 +47,7 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME("evevent")
-
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/* Local prototypes */
static acpi_status acpi_ev_fixed_event_initialize(void);
@@ -291,3 +291,5 @@ static u32 acpi_ev_fixed_event_dispatch(u32 event)
return ((acpi_gbl_fixed_event_handlers[event].
handler) (acpi_gbl_fixed_event_handlers[event].context));
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evglock.c b/drivers/acpi/acpica/evglock.c
index 5e5683c..cfeab38 100644
--- a/drivers/acpi/acpica/evglock.c
+++ b/drivers/acpi/acpica/evglock.c
@@ -48,7 +48,7 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME("evglock")
-
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/* Local prototypes */
static u32 acpi_ev_global_lock_handler(void *context);
@@ -339,3 +339,5 @@ acpi_status acpi_ev_release_global_lock(void)
acpi_os_release_mutex(acpi_gbl_global_lock_mutex->mutex.os_mutex);
return_ACPI_STATUS(status);
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c
index 9e88cb6..8ba0e5f 100644
--- a/drivers/acpi/acpica/evgpe.c
+++ b/drivers/acpi/acpica/evgpe.c
@@ -48,7 +48,7 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME("evgpe")
-
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/* Local prototypes */
static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context);
@@ -766,3 +766,5 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
return_UINT32(ACPI_INTERRUPT_HANDLED);
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c
index be75339..23a3ca8 100644
--- a/drivers/acpi/acpica/evgpeblk.c
+++ b/drivers/acpi/acpica/evgpeblk.c
@@ -48,7 +48,7 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME("evgpeblk")
-
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/* Local prototypes */
static acpi_status
acpi_ev_install_gpe_block(struct acpi_gpe_block_info *gpe_block,
@@ -504,3 +504,5 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
return_ACPI_STATUS(AE_OK);
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c
index adf7494..da0add8 100644
--- a/drivers/acpi/acpica/evgpeinit.c
+++ b/drivers/acpi/acpica/evgpeinit.c
@@ -48,7 +48,7 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME("evgpeinit")
-
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/*
* Note: History of _PRW support in ACPICA
*
@@ -440,3 +440,5 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
name, gpe_number));
return_ACPI_STATUS(AE_OK);
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c
index 2507393..3c43796 100644
--- a/drivers/acpi/acpica/evgpeutil.c
+++ b/drivers/acpi/acpica/evgpeutil.c
@@ -48,6 +48,7 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME("evgpeutil")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/*******************************************************************************
*
* FUNCTION: acpi_ev_walk_gpe_list
@@ -374,3 +375,5 @@ acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
return_ACPI_STATUS(AE_OK);
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c
index 84966f4..51ef9f5 100644
--- a/drivers/acpi/acpica/evmisc.c
+++ b/drivers/acpi/acpica/evmisc.c
@@ -108,27 +108,30 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node,
ACPI_FUNCTION_NAME(ev_queue_notify_request);
/*
- * For value 3 (Ejection Request), some device method may need to be run.
- * For value 2 (Device Wake) if _PRW exists, the _PS0 method may need
- * to be run.
+ * For value 0x03 (Ejection Request), may need to run a device method.
+ * For value 0x02 (Device Wake), if _PRW exists, may need to run
+ * the _PS0 method.
* For value 0x80 (Status Change) on the power button or sleep button,
- * initiate soft-off or sleep operation?
+ * initiate soft-off or sleep operation.
+ *
+ * For all cases, simply dispatch the notify to the handler.
*/
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Dispatching Notify on [%4.4s] Node %p Value 0x%2.2X (%s)\n",
- acpi_ut_get_node_name(node), node, notify_value,
- acpi_ut_get_notify_name(notify_value)));
+ "Dispatching Notify on [%4.4s] (%s) Value 0x%2.2X (%s) Node %p\n",
+ acpi_ut_get_node_name(node),
+ acpi_ut_get_type_name(node->type), notify_value,
+ acpi_ut_get_notify_name(notify_value), node));
/* Get the notify object attached to the NS Node */
obj_desc = acpi_ns_get_attached_object(node);
if (obj_desc) {
- /* We have the notify object, Get the right handler */
+ /* We have the notify object, Get the correct handler */
switch (node->type) {
- /* Notify allowed only on these types */
+ /* Notify is allowed only on these types */
case ACPI_TYPE_DEVICE:
case ACPI_TYPE_THERMAL:
@@ -152,7 +155,7 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node,
}
/*
- * If there is any handler to run, schedule the dispatcher.
+ * If there is a handler to run, schedule the dispatcher.
* Check for:
* 1) Global system notify handler
* 2) Global device notify handler
@@ -270,6 +273,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context)
acpi_ut_delete_generic_state(notify_info);
}
+#if (!ACPI_REDUCED_HARDWARE)
/******************************************************************************
*
* FUNCTION: acpi_ev_terminate
@@ -338,3 +342,5 @@ void acpi_ev_terminate(void)
}
return_VOID;
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evsci.c b/drivers/acpi/acpica/evsci.c
index 26065c6..6a57aa2 100644
--- a/drivers/acpi/acpica/evsci.c
+++ b/drivers/acpi/acpica/evsci.c
@@ -48,7 +48,7 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME("evsci")
-
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/* Local prototypes */
static u32 ACPI_SYSTEM_XFACE acpi_ev_sci_xrupt_handler(void *context);
@@ -181,3 +181,5 @@ acpi_status acpi_ev_remove_sci_handler(void)
return_ACPI_STATUS(status);
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c
index 61944e8..44bef57 100644
--- a/drivers/acpi/acpica/evxface.c
+++ b/drivers/acpi/acpica/evxface.c
@@ -51,222 +51,6 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME("evxface")
-/*******************************************************************************
- *
- * FUNCTION: acpi_install_exception_handler
- *
- * PARAMETERS: Handler - Pointer to the handler function for the
- * event
- *
- * RETURN: Status
- *
- * DESCRIPTION: Saves the pointer to the handler function
- *
- ******************************************************************************/
-#ifdef ACPI_FUTURE_USAGE
-acpi_status acpi_install_exception_handler(acpi_exception_handler handler)
-{
- acpi_status status;
-
- ACPI_FUNCTION_TRACE(acpi_install_exception_handler);
-
- status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- /* Don't allow two handlers. */
-
- if (acpi_gbl_exception_handler) {
- status = AE_ALREADY_EXISTS;
- goto cleanup;
- }
-
- /* Install the handler */
-
- acpi_gbl_exception_handler = handler;
-
- cleanup:
- (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
- return_ACPI_STATUS(status);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_install_exception_handler)
-#endif /* ACPI_FUTURE_USAGE */
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_install_global_event_handler
- *
- * PARAMETERS: Handler - Pointer to the global event handler function
- * Context - Value passed to the handler on each event
- *
- * RETURN: Status
- *
- * DESCRIPTION: Saves the pointer to the handler function. The global handler
- * is invoked upon each incoming GPE and Fixed Event. It is
- * invoked at interrupt level at the time of the event dispatch.
- * Can be used to update event counters, etc.
- *
- ******************************************************************************/
-acpi_status
-acpi_install_global_event_handler(ACPI_GBL_EVENT_HANDLER handler, void *context)
-{
- acpi_status status;
-
- ACPI_FUNCTION_TRACE(acpi_install_global_event_handler);
-
- /* Parameter validation */
-
- if (!handler) {
- return_ACPI_STATUS(AE_BAD_PARAMETER);
- }
-
- status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- /* Don't allow two handlers. */
-
- if (acpi_gbl_global_event_handler) {
- status = AE_ALREADY_EXISTS;
- goto cleanup;
- }
-
- acpi_gbl_global_event_handler = handler;
- acpi_gbl_global_event_handler_context = context;
-
- cleanup:
- (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
- return_ACPI_STATUS(status);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_install_global_event_handler)
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_install_fixed_event_handler
- *
- * PARAMETERS: Event - Event type to enable.
- * Handler - Pointer to the handler function for the
- * event
- * Context - Value passed to the handler on each GPE
- *
- * RETURN: Status
- *
- * DESCRIPTION: Saves the pointer to the handler function and then enables the
- * event.
- *
- ******************************************************************************/
-acpi_status
-acpi_install_fixed_event_handler(u32 event,
- acpi_event_handler handler, void *context)
-{
- acpi_status status;
-
- ACPI_FUNCTION_TRACE(acpi_install_fixed_event_handler);
-
- /* Parameter validation */
-
- if (event > ACPI_EVENT_MAX) {
- return_ACPI_STATUS(AE_BAD_PARAMETER);
- }
-
- status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- /* Don't allow two handlers. */
-
- if (NULL != acpi_gbl_fixed_event_handlers[event].handler) {
- status = AE_ALREADY_EXISTS;
- goto cleanup;
- }
-
- /* Install the handler before enabling the event */
-
- acpi_gbl_fixed_event_handlers[event].handler = handler;
- acpi_gbl_fixed_event_handlers[event].context = context;
-
- status = acpi_clear_event(event);
- if (ACPI_SUCCESS(status))
- status = acpi_enable_event(event, 0);
- if (ACPI_FAILURE(status)) {
- ACPI_WARNING((AE_INFO, "Could not enable fixed event 0x%X",
- event));
-
- /* Remove the handler */
-
- acpi_gbl_fixed_event_handlers[event].handler = NULL;
- acpi_gbl_fixed_event_handlers[event].context = NULL;
- } else {
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Enabled fixed event %X, Handler=%p\n", event,
- handler));
- }
-
- cleanup:
- (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
- return_ACPI_STATUS(status);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_install_fixed_event_handler)
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_remove_fixed_event_handler
- *
- * PARAMETERS: Event - Event type to disable.
- * Handler - Address of the handler
- *
- * RETURN: Status
- *
- * DESCRIPTION: Disables the event and unregisters the event handler.
- *
- ******************************************************************************/
-acpi_status
-acpi_remove_fixed_event_handler(u32 event, acpi_event_handler handler)
-{
- acpi_status status = AE_OK;
-
- ACPI_FUNCTION_TRACE(acpi_remove_fixed_event_handler);
-
- /* Parameter validation */
-
- if (event > ACPI_EVENT_MAX) {
- return_ACPI_STATUS(AE_BAD_PARAMETER);
- }
-
- status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- /* Disable the event before removing the handler */
-
- status = acpi_disable_event(event, 0);
-
- /* Always Remove the handler */
-
- acpi_gbl_fixed_event_handlers[event].handler = NULL;
- acpi_gbl_fixed_event_handlers[event].context = NULL;
-
- if (ACPI_FAILURE(status)) {
- ACPI_WARNING((AE_INFO,
- "Could not write to fixed event enable register 0x%X",
- event));
- } else {
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Disabled fixed event %X\n",
- event));
- }
-
- (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
- return_ACPI_STATUS(status);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_remove_fixed_event_handler)
/*******************************************************************************
*
@@ -334,6 +118,7 @@ acpi_add_handler_object(struct acpi_object_notify_handler *parent_obj,
return AE_OK;
}
+
/*******************************************************************************
*
* FUNCTION: acpi_install_notify_handler
@@ -705,6 +490,224 @@ ACPI_EXPORT_SYMBOL(acpi_remove_notify_handler)
/*******************************************************************************
*
+ * FUNCTION: acpi_install_exception_handler
+ *
+ * PARAMETERS: Handler - Pointer to the handler function for the
+ * event
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Saves the pointer to the handler function
+ *
+ ******************************************************************************/
+#ifdef ACPI_FUTURE_USAGE
+acpi_status acpi_install_exception_handler(acpi_exception_handler handler)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_install_exception_handler);
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Don't allow two handlers. */
+
+ if (acpi_gbl_exception_handler) {
+ status = AE_ALREADY_EXISTS;
+ goto cleanup;
+ }
+
+ /* Install the handler */
+
+ acpi_gbl_exception_handler = handler;
+
+ cleanup:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_exception_handler)
+#endif /* ACPI_FUTURE_USAGE */
+
+#if (!ACPI_REDUCED_HARDWARE)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_global_event_handler
+ *
+ * PARAMETERS: Handler - Pointer to the global event handler function
+ * Context - Value passed to the handler on each event
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Saves the pointer to the handler function. The global handler
+ * is invoked upon each incoming GPE and Fixed Event. It is
+ * invoked at interrupt level at the time of the event dispatch.
+ * Can be used to update event counters, etc.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_install_global_event_handler(ACPI_GBL_EVENT_HANDLER handler, void *context)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_install_global_event_handler);
+
+ /* Parameter validation */
+
+ if (!handler) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Don't allow two handlers. */
+
+ if (acpi_gbl_global_event_handler) {
+ status = AE_ALREADY_EXISTS;
+ goto cleanup;
+ }
+
+ acpi_gbl_global_event_handler = handler;
+ acpi_gbl_global_event_handler_context = context;
+
+ cleanup:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_global_event_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_install_fixed_event_handler
+ *
+ * PARAMETERS: Event - Event type to enable.
+ * Handler - Pointer to the handler function for the
+ * event
+ * Context - Value passed to the handler on each GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Saves the pointer to the handler function and then enables the
+ * event.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_install_fixed_event_handler(u32 event,
+ acpi_event_handler handler, void *context)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_install_fixed_event_handler);
+
+ /* Parameter validation */
+
+ if (event > ACPI_EVENT_MAX) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Don't allow two handlers. */
+
+ if (NULL != acpi_gbl_fixed_event_handlers[event].handler) {
+ status = AE_ALREADY_EXISTS;
+ goto cleanup;
+ }
+
+ /* Install the handler before enabling the event */
+
+ acpi_gbl_fixed_event_handlers[event].handler = handler;
+ acpi_gbl_fixed_event_handlers[event].context = context;
+
+ status = acpi_clear_event(event);
+ if (ACPI_SUCCESS(status))
+ status = acpi_enable_event(event, 0);
+ if (ACPI_FAILURE(status)) {
+ ACPI_WARNING((AE_INFO, "Could not enable fixed event 0x%X",
+ event));
+
+ /* Remove the handler */
+
+ acpi_gbl_fixed_event_handlers[event].handler = NULL;
+ acpi_gbl_fixed_event_handlers[event].context = NULL;
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Enabled fixed event %X, Handler=%p\n", event,
+ handler));
+ }
+
+ cleanup:
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_install_fixed_event_handler)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_remove_fixed_event_handler
+ *
+ * PARAMETERS: Event - Event type to disable.
+ * Handler - Address of the handler
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Disables the event and unregisters the event handler.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_remove_fixed_event_handler(u32 event, acpi_event_handler handler)
+{
+ acpi_status status = AE_OK;
+
+ ACPI_FUNCTION_TRACE(acpi_remove_fixed_event_handler);
+
+ /* Parameter validation */
+
+ if (event > ACPI_EVENT_MAX) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Disable the event before removing the handler */
+
+ status = acpi_disable_event(event, 0);
+
+ /* Always Remove the handler */
+
+ acpi_gbl_fixed_event_handlers[event].handler = NULL;
+ acpi_gbl_fixed_event_handlers[event].context = NULL;
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_WARNING((AE_INFO,
+ "Could not write to fixed event enable register 0x%X",
+ event));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Disabled fixed event %X\n",
+ event));
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_remove_fixed_event_handler)
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_install_gpe_handler
*
* PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT
@@ -984,3 +987,4 @@ acpi_status acpi_release_global_lock(u32 handle)
}
ACPI_EXPORT_SYMBOL(acpi_release_global_lock)
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
index 1768bbe..77cee5a 100644
--- a/drivers/acpi/acpica/evxfevnt.c
+++ b/drivers/acpi/acpica/evxfevnt.c
@@ -49,6 +49,7 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME("evxfevnt")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/*******************************************************************************
*
* FUNCTION: acpi_enable
@@ -352,3 +353,4 @@ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status)
}
ACPI_EXPORT_SYMBOL(acpi_get_event_status)
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c
index 33388fd..86f9b34 100644
--- a/drivers/acpi/acpica/evxfgpe.c
+++ b/drivers/acpi/acpica/evxfgpe.c
@@ -50,6 +50,7 @@
#define _COMPONENT ACPI_EVENTS
ACPI_MODULE_NAME("evxfgpe")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/******************************************************************************
*
* FUNCTION: acpi_update_all_gpes
@@ -695,3 +696,4 @@ acpi_get_gpe_device(u32 index, acpi_handle *gpe_device)
}
ACPI_EXPORT_SYMBOL(acpi_get_gpe_device)
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/hwacpi.c b/drivers/acpi/acpica/hwacpi.c
index d21ec5f..d0b9ed5 100644
--- a/drivers/acpi/acpica/hwacpi.c
+++ b/drivers/acpi/acpica/hwacpi.c
@@ -48,6 +48,7 @@
#define _COMPONENT ACPI_HARDWARE
ACPI_MODULE_NAME("hwacpi")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/******************************************************************************
*
* FUNCTION: acpi_hw_set_mode
@@ -166,3 +167,5 @@ u32 acpi_hw_get_mode(void)
return_UINT32(ACPI_SYS_MODE_LEGACY);
}
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c
new file mode 100644
index 0000000..29e8592
--- /dev/null
+++ b/drivers/acpi/acpica/hwesleep.c
@@ -0,0 +1,247 @@
+/******************************************************************************
+ *
+ * Name: hwesleep.c - ACPI Hardware Sleep/Wake Support functions for the
+ * extended FADT-V5 sleep registers.
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2012, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+#define _COMPONENT ACPI_HARDWARE
+ACPI_MODULE_NAME("hwesleep")
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_execute_sleep_method
+ *
+ * PARAMETERS: method_pathname - Pathname of method to execute
+ * integer_argument - Argument to pass to the method
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Execute a sleep/wake related method with one integer argument
+ * and no return value.
+ *
+ ******************************************************************************/
+void acpi_hw_execute_sleep_method(char *method_pathname, u32 integer_argument)
+{
+ struct acpi_object_list arg_list;
+ union acpi_object arg;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(hw_execute_sleep_method);
+
+ /* One argument, integer_argument; No return value expected */
+
+ arg_list.count = 1;
+ arg_list.pointer = &arg;
+ arg.type = ACPI_TYPE_INTEGER;
+ arg.integer.value = (u64)integer_argument;
+
+ status = acpi_evaluate_object(NULL, method_pathname, &arg_list, NULL);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+ ACPI_EXCEPTION((AE_INFO, status, "While executing method %s",
+ method_pathname));
+ }
+
+ return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_extended_sleep
+ *
+ * PARAMETERS: sleep_state - Which sleep state to enter
+ * Flags - ACPI_EXECUTE_GTS to run optional method
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enter a system sleep state via the extended FADT sleep
+ * registers (V5 FADT).
+ * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags)
+{
+ acpi_status status;
+ u8 sleep_type_value;
+ u64 sleep_status;
+
+ ACPI_FUNCTION_TRACE(hw_extended_sleep);
+
+ /* Extended sleep registers must be valid */
+
+ if (!acpi_gbl_FADT.sleep_control.address ||
+ !acpi_gbl_FADT.sleep_status.address) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /* Clear wake status (WAK_STS) */
+
+ status = acpi_write(ACPI_X_WAKE_STATUS, &acpi_gbl_FADT.sleep_status);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ acpi_gbl_system_awake_and_running = FALSE;
+
+ /* Optionally execute _GTS (Going To Sleep) */
+
+ if (flags & ACPI_EXECUTE_GTS) {
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__GTS, sleep_state);
+ }
+
+ /* Flush caches, as per ACPI specification */
+
+ ACPI_FLUSH_CPU_CACHE();
+
+ /*
+ * Set the SLP_TYP and SLP_EN bits.
+ *
+ * Note: We only use the first value returned by the \_Sx method
+ * (acpi_gbl_sleep_type_a) - As per ACPI specification.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_INIT,
+ "Entering sleep state [S%u]\n", sleep_state));
+
+ sleep_type_value =
+ ((acpi_gbl_sleep_type_a << ACPI_X_SLEEP_TYPE_POSITION) &
+ ACPI_X_SLEEP_TYPE_MASK);
+
+ status = acpi_write((sleep_type_value | ACPI_X_SLEEP_ENABLE),
+ &acpi_gbl_FADT.sleep_control);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Wait for transition back to Working State */
+
+ do {
+ status = acpi_read(&sleep_status, &acpi_gbl_FADT.sleep_status);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ } while (!(((u8)sleep_status) & ACPI_X_WAKE_STATUS));
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_extended_wake_prep
+ *
+ * PARAMETERS: sleep_state - Which sleep state we just exited
+ * Flags - ACPI_EXECUTE_BFS to run optional method
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform first part of OS-independent ACPI cleanup after
+ * a sleep. Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags)
+{
+ acpi_status status;
+ u8 sleep_type_value;
+
+ ACPI_FUNCTION_TRACE(hw_extended_wake_prep);
+
+ status = acpi_get_sleep_type_data(ACPI_STATE_S0,
+ &acpi_gbl_sleep_type_a,
+ &acpi_gbl_sleep_type_b);
+ if (ACPI_SUCCESS(status)) {
+ sleep_type_value =
+ ((acpi_gbl_sleep_type_a << ACPI_X_SLEEP_TYPE_POSITION) &
+ ACPI_X_SLEEP_TYPE_MASK);
+
+ (void)acpi_write((sleep_type_value | ACPI_X_SLEEP_ENABLE),
+ &acpi_gbl_FADT.sleep_control);
+ }
+
+ /* Optionally execute _BFS (Back From Sleep) */
+
+ if (flags & ACPI_EXECUTE_BFS) {
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__BFS, sleep_state);
+ }
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_extended_wake
+ *
+ * PARAMETERS: sleep_state - Which sleep state we just exited
+ * Flags - Reserved, set to zero
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
+ * Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_hw_extended_wake(u8 sleep_state, u8 flags)
+{
+ ACPI_FUNCTION_TRACE(hw_extended_wake);
+
+ /* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */
+
+ acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID;
+
+ /* Execute the wake methods */
+
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WAKING);
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__WAK, sleep_state);
+
+ /*
+ * Some BIOS code assumes that WAK_STS will be cleared on resume
+ * and use it to determine whether the system is rebooting or
+ * resuming. Clear WAK_STS for compatibility.
+ */
+ (void)acpi_write(ACPI_X_WAKE_STATUS, &acpi_gbl_FADT.sleep_status);
+ acpi_gbl_system_awake_and_running = TRUE;
+
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WORKING);
+ return_ACPI_STATUS(AE_OK);
+}
diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c
index 1a6894a..25bd28c 100644
--- a/drivers/acpi/acpica/hwgpe.c
+++ b/drivers/acpi/acpica/hwgpe.c
@@ -48,7 +48,7 @@
#define _COMPONENT ACPI_HARDWARE
ACPI_MODULE_NAME("hwgpe")
-
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/* Local prototypes */
static acpi_status
acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
@@ -479,3 +479,5 @@ acpi_status acpi_hw_enable_all_wakeup_gpes(void)
status = acpi_ev_walk_gpe_list(acpi_hw_enable_wakeup_gpe_block, NULL);
return_ACPI_STATUS(status);
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c
index 4ea4eeb5..6b6c83b 100644
--- a/drivers/acpi/acpica/hwregs.c
+++ b/drivers/acpi/acpica/hwregs.c
@@ -51,6 +51,7 @@
#define _COMPONENT ACPI_HARDWARE
ACPI_MODULE_NAME("hwregs")
+#if (!ACPI_REDUCED_HARDWARE)
/* Local Prototypes */
static acpi_status
acpi_hw_read_multiple(u32 *value,
@@ -62,6 +63,8 @@ acpi_hw_write_multiple(u32 value,
struct acpi_generic_address *register_a,
struct acpi_generic_address *register_b);
+#endif /* !ACPI_REDUCED_HARDWARE */
+
/******************************************************************************
*
* FUNCTION: acpi_hw_validate_register
@@ -154,6 +157,7 @@ acpi_hw_validate_register(struct acpi_generic_address *reg,
acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg)
{
u64 address;
+ u64 value64;
acpi_status status;
ACPI_FUNCTION_NAME(hw_read);
@@ -175,7 +179,9 @@ acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg)
*/
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
status = acpi_os_read_memory((acpi_physical_address)
- address, value, reg->bit_width);
+ address, &value64, reg->bit_width);
+
+ *value = (u32)value64;
} else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
status = acpi_hw_read_port((acpi_io_address)
@@ -225,7 +231,8 @@ acpi_status acpi_hw_write(u32 value, struct acpi_generic_address *reg)
*/
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
status = acpi_os_write_memory((acpi_physical_address)
- address, value, reg->bit_width);
+ address, (u64)value,
+ reg->bit_width);
} else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
status = acpi_hw_write_port((acpi_io_address)
@@ -240,6 +247,7 @@ acpi_status acpi_hw_write(u32 value, struct acpi_generic_address *reg)
return (status);
}
+#if (!ACPI_REDUCED_HARDWARE)
/*******************************************************************************
*
* FUNCTION: acpi_hw_clear_acpi_status
@@ -285,7 +293,7 @@ exit:
/*******************************************************************************
*
- * FUNCTION: acpi_hw_get_register_bit_mask
+ * FUNCTION: acpi_hw_get_bit_register_info
*
* PARAMETERS: register_id - Index of ACPI Register to access
*
@@ -658,3 +666,5 @@ acpi_hw_write_multiple(u32 value,
return (status);
}
+
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c
index 3c4a922..0ed85ca 100644
--- a/drivers/acpi/acpica/hwsleep.c
+++ b/drivers/acpi/acpica/hwsleep.c
@@ -1,7 +1,7 @@
-
/******************************************************************************
*
- * Name: hwsleep.c - ACPI Hardware Sleep/Wake Interface
+ * Name: hwsleep.c - ACPI Hardware Sleep/Wake Support functions for the
+ * original/legacy sleep/PM registers.
*
*****************************************************************************/
@@ -43,213 +43,37 @@
*/
#include <acpi/acpi.h>
+#include <linux/acpi.h>
#include "accommon.h"
-#include "actables.h"
-#include <linux/tboot.h>
#include <linux/module.h>
#define _COMPONENT ACPI_HARDWARE
ACPI_MODULE_NAME("hwsleep")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/*******************************************************************************
*
- * FUNCTION: acpi_set_firmware_waking_vector
- *
- * PARAMETERS: physical_address - 32-bit physical address of ACPI real mode
- * entry point.
- *
- * RETURN: Status
- *
- * DESCRIPTION: Sets the 32-bit firmware_waking_vector field of the FACS
- *
- ******************************************************************************/
-acpi_status
-acpi_set_firmware_waking_vector(u32 physical_address)
-{
- ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector);
-
-
- /*
- * According to the ACPI specification 2.0c and later, the 64-bit
- * waking vector should be cleared and the 32-bit waking vector should
- * be used, unless we want the wake-up code to be called by the BIOS in
- * Protected Mode. Some systems (for example HP dv5-1004nr) are known
- * to fail to resume if the 64-bit vector is used.
- */
-
- /* Set the 32-bit vector */
-
- acpi_gbl_FACS->firmware_waking_vector = physical_address;
-
- /* Clear the 64-bit vector if it exists */
-
- if ((acpi_gbl_FACS->length > 32) && (acpi_gbl_FACS->version >= 1)) {
- acpi_gbl_FACS->xfirmware_waking_vector = 0;
- }
-
- return_ACPI_STATUS(AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
-
-#if ACPI_MACHINE_WIDTH == 64
-/*******************************************************************************
- *
- * FUNCTION: acpi_set_firmware_waking_vector64
- *
- * PARAMETERS: physical_address - 64-bit physical address of ACPI protected
- * mode entry point.
- *
- * RETURN: Status
- *
- * DESCRIPTION: Sets the 64-bit X_firmware_waking_vector field of the FACS, if
- * it exists in the table. This function is intended for use with
- * 64-bit host operating systems.
- *
- ******************************************************************************/
-acpi_status
-acpi_set_firmware_waking_vector64(u64 physical_address)
-{
- ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector64);
-
-
- /* Determine if the 64-bit vector actually exists */
-
- if ((acpi_gbl_FACS->length <= 32) || (acpi_gbl_FACS->version < 1)) {
- return_ACPI_STATUS(AE_NOT_EXIST);
- }
-
- /* Clear 32-bit vector, set the 64-bit X_ vector */
-
- acpi_gbl_FACS->firmware_waking_vector = 0;
- acpi_gbl_FACS->xfirmware_waking_vector = physical_address;
-
- return_ACPI_STATUS(AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector64)
-#endif
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_enter_sleep_state_prep
- *
- * PARAMETERS: sleep_state - Which sleep state to enter
- *
- * RETURN: Status
- *
- * DESCRIPTION: Prepare to enter a system sleep state (see ACPI 2.0 spec p 231)
- * This function must execute with interrupts enabled.
- * We break sleeping into 2 stages so that OSPM can handle
- * various OS-specific tasks between the two steps.
- *
- ******************************************************************************/
-acpi_status acpi_enter_sleep_state_prep(u8 sleep_state)
-{
- acpi_status status;
- struct acpi_object_list arg_list;
- union acpi_object arg;
-
- ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_prep);
-
- /* _PSW methods could be run here to enable wake-on keyboard, LAN, etc. */
-
- status = acpi_get_sleep_type_data(sleep_state,
- &acpi_gbl_sleep_type_a,
- &acpi_gbl_sleep_type_b);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- /* Setup parameter object */
-
- arg_list.count = 1;
- arg_list.pointer = &arg;
-
- arg.type = ACPI_TYPE_INTEGER;
- arg.integer.value = sleep_state;
-
- /* Run the _PTS method */
-
- status = acpi_evaluate_object(NULL, METHOD_NAME__PTS, &arg_list, NULL);
- if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
- return_ACPI_STATUS(status);
- }
-
- /* Setup the argument to _SST */
-
- switch (sleep_state) {
- case ACPI_STATE_S0:
- arg.integer.value = ACPI_SST_WORKING;
- break;
-
- case ACPI_STATE_S1:
- case ACPI_STATE_S2:
- case ACPI_STATE_S3:
- arg.integer.value = ACPI_SST_SLEEPING;
- break;
-
- case ACPI_STATE_S4:
- arg.integer.value = ACPI_SST_SLEEP_CONTEXT;
- break;
-
- default:
- arg.integer.value = ACPI_SST_INDICATOR_OFF; /* Default is off */
- break;
- }
-
- /*
- * Set the system indicators to show the desired sleep state.
- * _SST is an optional method (return no error if not found)
- */
- status = acpi_evaluate_object(NULL, METHOD_NAME__SST, &arg_list, NULL);
- if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
- ACPI_EXCEPTION((AE_INFO, status,
- "While executing method _SST"));
- }
-
- return_ACPI_STATUS(AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep)
-
-static unsigned int gts, bfs;
-module_param(gts, uint, 0644);
-module_param(bfs, uint, 0644);
-MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend.");
-MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".);
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_enter_sleep_state
+ * FUNCTION: acpi_hw_legacy_sleep
*
* PARAMETERS: sleep_state - Which sleep state to enter
+ * Flags - ACPI_EXECUTE_GTS to run optional method
*
* RETURN: Status
*
- * DESCRIPTION: Enter a system sleep state (see ACPI 2.0 spec p 231)
+ * DESCRIPTION: Enter a system sleep state via the legacy FADT PM registers
* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
*
******************************************************************************/
-acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
+acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags)
{
- u32 pm1a_control;
- u32 pm1b_control;
struct acpi_bit_register_info *sleep_type_reg_info;
struct acpi_bit_register_info *sleep_enable_reg_info;
+ u32 pm1a_control;
+ u32 pm1b_control;
u32 in_value;
- struct acpi_object_list arg_list;
- union acpi_object arg;
acpi_status status;
- ACPI_FUNCTION_TRACE(acpi_enter_sleep_state);
-
- if ((acpi_gbl_sleep_type_a > ACPI_SLEEP_TYPE_MAX) ||
- (acpi_gbl_sleep_type_b > ACPI_SLEEP_TYPE_MAX)) {
- ACPI_ERROR((AE_INFO, "Sleep values out of range: A=0x%X B=0x%X",
- acpi_gbl_sleep_type_a, acpi_gbl_sleep_type_b));
- return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
- }
+ ACPI_FUNCTION_TRACE(hw_legacy_sleep);
sleep_type_reg_info =
acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_TYPE);
@@ -271,6 +95,18 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
return_ACPI_STATUS(status);
}
+ if (sleep_state != ACPI_STATE_S5) {
+ /*
+ * Disable BM arbitration. This feature is contained within an
+ * optional register (PM2 Control), so ignore a BAD_ADDRESS
+ * exception.
+ */
+ status = acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1);
+ if (ACPI_FAILURE(status) && (status != AE_BAD_ADDRESS)) {
+ return_ACPI_STATUS(status);
+ }
+ }
+
/*
* 1) Disable/Clear all GPEs
* 2) Enable all wakeup GPEs
@@ -286,18 +122,10 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
return_ACPI_STATUS(status);
}
- if (gts) {
- /* Execute the _GTS method */
-
- arg_list.count = 1;
- arg_list.pointer = &arg;
- arg.type = ACPI_TYPE_INTEGER;
- arg.integer.value = sleep_state;
+ /* Optionally execute _GTS (Going To Sleep) */
- status = acpi_evaluate_object(NULL, METHOD_NAME__GTS, &arg_list, NULL);
- if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
- return_ACPI_STATUS(status);
- }
+ if (flags & ACPI_EXECUTE_GTS) {
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__GTS, sleep_state);
}
/* Get current value of PM1A control */
@@ -344,8 +172,12 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
ACPI_FLUSH_CPU_CACHE();
- tboot_sleep(sleep_state, pm1a_control, pm1b_control);
-
+ status = acpi_os_prepare_sleep(sleep_state, pm1a_control,
+ pm1b_control);
+ if (ACPI_SKIP(status))
+ return_ACPI_STATUS(AE_OK);
+ if (ACPI_FAILURE(status))
+ return_ACPI_STATUS(status);
/* Write #2: Write both SLP_TYP + SLP_EN */
status = acpi_hw_write_pm1_control(pm1a_control, pm1b_control);
@@ -375,114 +207,44 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
}
}
- /* Wait until we enter sleep state */
-
- do {
- status = acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS,
- &in_value);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- /* Spin until we wake */
-
- } while (!in_value);
-
- return_ACPI_STATUS(AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state)
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_enter_sleep_state_s4bios
- *
- * PARAMETERS: None
- *
- * RETURN: Status
- *
- * DESCRIPTION: Perform a S4 bios request.
- * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
- *
- ******************************************************************************/
-acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void)
-{
- u32 in_value;
- acpi_status status;
-
- ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_s4bios);
-
- /* Clear the wake status bit (PM1) */
-
- status =
- acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- status = acpi_hw_clear_acpi_status();
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- /*
- * 1) Disable/Clear all GPEs
- * 2) Enable all wakeup GPEs
- */
- status = acpi_hw_disable_all_gpes();
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
- acpi_gbl_system_awake_and_running = FALSE;
-
- status = acpi_hw_enable_all_wakeup_gpes();
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- ACPI_FLUSH_CPU_CACHE();
-
- status = acpi_hw_write_port(acpi_gbl_FADT.smi_command,
- (u32) acpi_gbl_FADT.S4bios_request, 8);
+ /* Wait for transition back to Working State */
do {
- acpi_os_stall(1000);
status =
acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS, &in_value);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
+
} while (!in_value);
return_ACPI_STATUS(AE_OK);
}
-ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios)
-
/*******************************************************************************
*
- * FUNCTION: acpi_leave_sleep_state_prep
+ * FUNCTION: acpi_hw_legacy_wake_prep
*
- * PARAMETERS: sleep_state - Which sleep state we are exiting
+ * PARAMETERS: sleep_state - Which sleep state we just exited
+ * Flags - ACPI_EXECUTE_BFS to run optional method
*
* RETURN: Status
*
* DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a
* sleep.
- * Called with interrupts DISABLED.
+ * Called with interrupts ENABLED.
*
******************************************************************************/
-acpi_status acpi_leave_sleep_state_prep(u8 sleep_state)
+
+acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags)
{
- struct acpi_object_list arg_list;
- union acpi_object arg;
acpi_status status;
struct acpi_bit_register_info *sleep_type_reg_info;
struct acpi_bit_register_info *sleep_enable_reg_info;
u32 pm1a_control;
u32 pm1b_control;
- ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep);
+ ACPI_FUNCTION_TRACE(hw_legacy_wake_prep);
/*
* Set SLP_TYPE and SLP_EN to state S0.
@@ -525,27 +287,20 @@ acpi_status acpi_leave_sleep_state_prep(u8 sleep_state)
}
}
- if (bfs) {
- /* Execute the _BFS method */
+ /* Optionally execute _BFS (Back From Sleep) */
- arg_list.count = 1;
- arg_list.pointer = &arg;
- arg.type = ACPI_TYPE_INTEGER;
- arg.integer.value = sleep_state;
-
- status = acpi_evaluate_object(NULL, METHOD_NAME__BFS, &arg_list, NULL);
- if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
- ACPI_EXCEPTION((AE_INFO, status, "During Method _BFS"));
- }
+ if (flags & ACPI_EXECUTE_BFS) {
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__BFS, sleep_state);
}
return_ACPI_STATUS(status);
}
/*******************************************************************************
*
- * FUNCTION: acpi_leave_sleep_state
+ * FUNCTION: acpi_hw_legacy_wake
*
* PARAMETERS: sleep_state - Which sleep state we just exited
+ * Flags - Reserved, set to zero
*
* RETURN: Status
*
@@ -553,31 +308,17 @@ acpi_status acpi_leave_sleep_state_prep(u8 sleep_state)
* Called with interrupts ENABLED.
*
******************************************************************************/
-acpi_status acpi_leave_sleep_state(u8 sleep_state)
+
+acpi_status acpi_hw_legacy_wake(u8 sleep_state, u8 flags)
{
- struct acpi_object_list arg_list;
- union acpi_object arg;
acpi_status status;
- ACPI_FUNCTION_TRACE(acpi_leave_sleep_state);
+ ACPI_FUNCTION_TRACE(hw_legacy_wake);
/* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */
acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID;
-
- /* Setup parameter object */
-
- arg_list.count = 1;
- arg_list.pointer = &arg;
- arg.type = ACPI_TYPE_INTEGER;
-
- /* Ignore any errors from these methods */
-
- arg.integer.value = ACPI_SST_WAKING;
- status = acpi_evaluate_object(NULL, METHOD_NAME__SST, &arg_list, NULL);
- if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
- ACPI_EXCEPTION((AE_INFO, status, "During Method _SST"));
- }
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WAKING);
/*
* GPEs must be enabled before _WAK is called as GPEs
@@ -591,46 +332,50 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state)
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
+
status = acpi_hw_enable_all_runtime_gpes();
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
- arg.integer.value = sleep_state;
- status = acpi_evaluate_object(NULL, METHOD_NAME__WAK, &arg_list, NULL);
- if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
- ACPI_EXCEPTION((AE_INFO, status, "During Method _WAK"));
- }
- /* TBD: _WAK "sometimes" returns stuff - do we want to look at it? */
+ /*
+ * Now we can execute _WAK, etc. Some machines require that the GPEs
+ * are enabled before the wake methods are executed.
+ */
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__WAK, sleep_state);
/*
- * Some BIOSes assume that WAK_STS will be cleared on resume and use
- * it to determine whether the system is rebooting or resuming. Clear
- * it for compatibility.
+ * Some BIOS code assumes that WAK_STS will be cleared on resume
+ * and use it to determine whether the system is rebooting or
+ * resuming. Clear WAK_STS for compatibility.
*/
acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, 1);
-
acpi_gbl_system_awake_and_running = TRUE;
/* Enable power button */
(void)
acpi_write_bit_register(acpi_gbl_fixed_event_info
- [ACPI_EVENT_POWER_BUTTON].
- enable_register_id, ACPI_ENABLE_EVENT);
+ [ACPI_EVENT_POWER_BUTTON].
+ enable_register_id, ACPI_ENABLE_EVENT);
(void)
acpi_write_bit_register(acpi_gbl_fixed_event_info
- [ACPI_EVENT_POWER_BUTTON].
- status_register_id, ACPI_CLEAR_STATUS);
+ [ACPI_EVENT_POWER_BUTTON].
+ status_register_id, ACPI_CLEAR_STATUS);
- arg.integer.value = ACPI_SST_WORKING;
- status = acpi_evaluate_object(NULL, METHOD_NAME__SST, &arg_list, NULL);
- if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
- ACPI_EXCEPTION((AE_INFO, status, "During Method _SST"));
+ /*
+ * Enable BM arbitration. This feature is contained within an
+ * optional register (PM2 Control), so ignore a BAD_ADDRESS
+ * exception.
+ */
+ status = acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0);
+ if (ACPI_FAILURE(status) && (status != AE_BAD_ADDRESS)) {
+ return_ACPI_STATUS(status);
}
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WORKING);
return_ACPI_STATUS(status);
}
-ACPI_EXPORT_SYMBOL(acpi_leave_sleep_state)
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/hwtimer.c b/drivers/acpi/acpica/hwtimer.c
index d4973d9..f1b2c3b 100644
--- a/drivers/acpi/acpica/hwtimer.c
+++ b/drivers/acpi/acpica/hwtimer.c
@@ -49,6 +49,7 @@
#define _COMPONENT ACPI_HARDWARE
ACPI_MODULE_NAME("hwtimer")
+#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
/******************************************************************************
*
* FUNCTION: acpi_get_timer_resolution
@@ -187,3 +188,4 @@ acpi_get_timer_duration(u32 start_ticks, u32 end_ticks, u32 * time_elapsed)
}
ACPI_EXPORT_SYMBOL(acpi_get_timer_duration)
+#endif /* !ACPI_REDUCED_HARDWARE */
diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c
index 9d38eb6..ab513a9 100644
--- a/drivers/acpi/acpica/hwxface.c
+++ b/drivers/acpi/acpica/hwxface.c
@@ -74,8 +74,7 @@ acpi_status acpi_reset(void)
/* Check if the reset register is supported */
- if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) ||
- !reset_reg->address) {
+ if (!reset_reg->address) {
return_ACPI_STATUS(AE_NOT_EXIST);
}
@@ -138,11 +137,6 @@ acpi_status acpi_read(u64 *return_value, struct acpi_generic_address *reg)
return (status);
}
- width = reg->bit_width;
- if (width == 64) {
- width = 32; /* Break into two 32-bit transfers */
- }
-
/* Initialize entire 64-bit return value to zero */
*return_value = 0;
@@ -154,24 +148,17 @@ acpi_status acpi_read(u64 *return_value, struct acpi_generic_address *reg)
*/
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
status = acpi_os_read_memory((acpi_physical_address)
- address, &value, width);
+ address, return_value,
+ reg->bit_width);
if (ACPI_FAILURE(status)) {
return (status);
}
- *return_value = value;
-
- if (reg->bit_width == 64) {
-
- /* Read the top 32 bits */
+ } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
- status = acpi_os_read_memory((acpi_physical_address)
- (address + 4), &value, 32);
- if (ACPI_FAILURE(status)) {
- return (status);
- }
- *return_value |= ((u64)value << 32);
+ width = reg->bit_width;
+ if (width == 64) {
+ width = 32; /* Break into two 32-bit transfers */
}
- } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
status = acpi_hw_read_port((acpi_io_address)
address, &value, width);
@@ -231,32 +218,22 @@ acpi_status acpi_write(u64 value, struct acpi_generic_address *reg)
return (status);
}
- width = reg->bit_width;
- if (width == 64) {
- width = 32; /* Break into two 32-bit transfers */
- }
-
/*
* Two address spaces supported: Memory or IO. PCI_Config is
* not supported here because the GAS structure is insufficient
*/
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
status = acpi_os_write_memory((acpi_physical_address)
- address, ACPI_LODWORD(value),
- width);
+ address, value, reg->bit_width);
if (ACPI_FAILURE(status)) {
return (status);
}
+ } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
- if (reg->bit_width == 64) {
- status = acpi_os_write_memory((acpi_physical_address)
- (address + 4),
- ACPI_HIDWORD(value), 32);
- if (ACPI_FAILURE(status)) {
- return (status);
- }
+ width = reg->bit_width;
+ if (width == 64) {
+ width = 32; /* Break into two 32-bit transfers */
}
- } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
status = acpi_hw_write_port((acpi_io_address)
address, ACPI_LODWORD(value),
@@ -286,6 +263,7 @@ acpi_status acpi_write(u64 value, struct acpi_generic_address *reg)
ACPI_EXPORT_SYMBOL(acpi_write)
+#if (!ACPI_REDUCED_HARDWARE)
/*******************************************************************************
*
* FUNCTION: acpi_read_bit_register
@@ -453,7 +431,7 @@ unlock_and_exit:
}
ACPI_EXPORT_SYMBOL(acpi_write_bit_register)
-
+#endif /* !ACPI_REDUCED_HARDWARE */
/*******************************************************************************
*
* FUNCTION: acpi_get_sleep_type_data
diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c
new file mode 100644
index 0000000..762d059
--- /dev/null
+++ b/drivers/acpi/acpica/hwxfsleep.c
@@ -0,0 +1,431 @@
+/******************************************************************************
+ *
+ * Name: hwxfsleep.c - ACPI Hardware Sleep/Wake External Interfaces
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2012, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include <linux/module.h>
+
+#define _COMPONENT ACPI_HARDWARE
+ACPI_MODULE_NAME("hwxfsleep")
+
+/* Local prototypes */
+static acpi_status
+acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id);
+
+/*
+ * Dispatch table used to efficiently branch to the various sleep
+ * functions.
+ */
+#define ACPI_SLEEP_FUNCTION_ID 0
+#define ACPI_WAKE_PREP_FUNCTION_ID 1
+#define ACPI_WAKE_FUNCTION_ID 2
+
+/* Legacy functions are optional, based upon ACPI_REDUCED_HARDWARE */
+
+static struct acpi_sleep_functions acpi_sleep_dispatch[] = {
+ {ACPI_HW_OPTIONAL_FUNCTION(acpi_hw_legacy_sleep),
+ acpi_hw_extended_sleep},
+ {ACPI_HW_OPTIONAL_FUNCTION(acpi_hw_legacy_wake_prep),
+ acpi_hw_extended_wake_prep},
+ {ACPI_HW_OPTIONAL_FUNCTION(acpi_hw_legacy_wake), acpi_hw_extended_wake}
+};
+
+/*
+ * These functions are removed for the ACPI_REDUCED_HARDWARE case:
+ * acpi_set_firmware_waking_vector
+ * acpi_set_firmware_waking_vector64
+ * acpi_enter_sleep_state_s4bios
+ */
+
+#if (!ACPI_REDUCED_HARDWARE)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_set_firmware_waking_vector
+ *
+ * PARAMETERS: physical_address - 32-bit physical address of ACPI real mode
+ * entry point.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Sets the 32-bit firmware_waking_vector field of the FACS
+ *
+ ******************************************************************************/
+
+acpi_status acpi_set_firmware_waking_vector(u32 physical_address)
+{
+ ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector);
+
+
+ /*
+ * According to the ACPI specification 2.0c and later, the 64-bit
+ * waking vector should be cleared and the 32-bit waking vector should
+ * be used, unless we want the wake-up code to be called by the BIOS in
+ * Protected Mode. Some systems (for example HP dv5-1004nr) are known
+ * to fail to resume if the 64-bit vector is used.
+ */
+
+ /* Set the 32-bit vector */
+
+ acpi_gbl_FACS->firmware_waking_vector = physical_address;
+
+ /* Clear the 64-bit vector if it exists */
+
+ if ((acpi_gbl_FACS->length > 32) && (acpi_gbl_FACS->version >= 1)) {
+ acpi_gbl_FACS->xfirmware_waking_vector = 0;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
+
+#if ACPI_MACHINE_WIDTH == 64
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_set_firmware_waking_vector64
+ *
+ * PARAMETERS: physical_address - 64-bit physical address of ACPI protected
+ * mode entry point.
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Sets the 64-bit X_firmware_waking_vector field of the FACS, if
+ * it exists in the table. This function is intended for use with
+ * 64-bit host operating systems.
+ *
+ ******************************************************************************/
+acpi_status acpi_set_firmware_waking_vector64(u64 physical_address)
+{
+ ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector64);
+
+
+ /* Determine if the 64-bit vector actually exists */
+
+ if ((acpi_gbl_FACS->length <= 32) || (acpi_gbl_FACS->version < 1)) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /* Clear 32-bit vector, set the 64-bit X_ vector */
+
+ acpi_gbl_FACS->firmware_waking_vector = 0;
+ acpi_gbl_FACS->xfirmware_waking_vector = physical_address;
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector64)
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_enter_sleep_state_s4bios
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform a S4 bios request.
+ * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void)
+{
+ u32 in_value;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_s4bios);
+
+ /* Clear the wake status bit (PM1) */
+
+ status =
+ acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_hw_clear_acpi_status();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * 1) Disable/Clear all GPEs
+ * 2) Enable all wakeup GPEs
+ */
+ status = acpi_hw_disable_all_gpes();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ acpi_gbl_system_awake_and_running = FALSE;
+
+ status = acpi_hw_enable_all_wakeup_gpes();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ ACPI_FLUSH_CPU_CACHE();
+
+ status = acpi_hw_write_port(acpi_gbl_FADT.smi_command,
+ (u32)acpi_gbl_FADT.S4bios_request, 8);
+
+ do {
+ acpi_os_stall(1000);
+ status =
+ acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS, &in_value);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ } while (!in_value);
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios)
+#endif /* !ACPI_REDUCED_HARDWARE */
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_hw_sleep_dispatch
+ *
+ * PARAMETERS: sleep_state - Which sleep state to enter/exit
+ * function_id - Sleep, wake_prep, or Wake
+ *
+ * RETURN: Status from the invoked sleep handling function.
+ *
+ * DESCRIPTION: Dispatch a sleep/wake request to the appropriate handling
+ * function.
+ *
+ ******************************************************************************/
+static acpi_status
+acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id)
+{
+ acpi_status status;
+ struct acpi_sleep_functions *sleep_functions =
+ &acpi_sleep_dispatch[function_id];
+
+#if (!ACPI_REDUCED_HARDWARE)
+
+ /*
+ * If the Hardware Reduced flag is set (from the FADT), we must
+ * use the extended sleep registers
+ */
+ if (acpi_gbl_reduced_hardware || acpi_gbl_FADT.sleep_control.address) {
+ status = sleep_functions->extended_function(sleep_state, flags);
+ } else {
+ /* Legacy sleep */
+
+ status = sleep_functions->legacy_function(sleep_state, flags);
+ }
+
+ return (status);
+
+#else
+ /*
+ * For the case where reduced-hardware-only code is being generated,
+ * we know that only the extended sleep registers are available
+ */
+ status = sleep_functions->extended_function(sleep_state, flags);
+ return (status);
+
+#endif /* !ACPI_REDUCED_HARDWARE */
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_enter_sleep_state_prep
+ *
+ * PARAMETERS: sleep_state - Which sleep state to enter
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Prepare to enter a system sleep state.
+ * This function must execute with interrupts enabled.
+ * We break sleeping into 2 stages so that OSPM can handle
+ * various OS-specific tasks between the two steps.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_enter_sleep_state_prep(u8 sleep_state)
+{
+ acpi_status status;
+ struct acpi_object_list arg_list;
+ union acpi_object arg;
+ u32 sst_value;
+
+ ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_prep);
+
+ status = acpi_get_sleep_type_data(sleep_state,
+ &acpi_gbl_sleep_type_a,
+ &acpi_gbl_sleep_type_b);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Execute the _PTS method (Prepare To Sleep) */
+
+ arg_list.count = 1;
+ arg_list.pointer = &arg;
+ arg.type = ACPI_TYPE_INTEGER;
+ arg.integer.value = sleep_state;
+
+ status =
+ acpi_evaluate_object(NULL, METHOD_PATHNAME__PTS, &arg_list, NULL);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Setup the argument to the _SST method (System STatus) */
+
+ switch (sleep_state) {
+ case ACPI_STATE_S0:
+ sst_value = ACPI_SST_WORKING;
+ break;
+
+ case ACPI_STATE_S1:
+ case ACPI_STATE_S2:
+ case ACPI_STATE_S3:
+ sst_value = ACPI_SST_SLEEPING;
+ break;
+
+ case ACPI_STATE_S4:
+ sst_value = ACPI_SST_SLEEP_CONTEXT;
+ break;
+
+ default:
+ sst_value = ACPI_SST_INDICATOR_OFF; /* Default is off */
+ break;
+ }
+
+ /*
+ * Set the system indicators to show the desired sleep state.
+ * _SST is an optional method (return no error if not found)
+ */
+ acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, sst_value);
+ return_ACPI_STATUS(AE_OK);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_enter_sleep_state
+ *
+ * PARAMETERS: sleep_state - Which sleep state to enter
+ * Flags - ACPI_EXECUTE_GTS to run optional method
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Enter a system sleep state (see ACPI 2.0 spec p 231)
+ * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
+ *
+ ******************************************************************************/
+acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_enter_sleep_state);
+
+ if ((acpi_gbl_sleep_type_a > ACPI_SLEEP_TYPE_MAX) ||
+ (acpi_gbl_sleep_type_b > ACPI_SLEEP_TYPE_MAX)) {
+ ACPI_ERROR((AE_INFO, "Sleep values out of range: A=0x%X B=0x%X",
+ acpi_gbl_sleep_type_a, acpi_gbl_sleep_type_b));
+ return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
+ }
+
+ status =
+ acpi_hw_sleep_dispatch(sleep_state, flags, ACPI_SLEEP_FUNCTION_ID);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_leave_sleep_state_prep
+ *
+ * PARAMETERS: sleep_state - Which sleep state we are exiting
+ * Flags - ACPI_EXECUTE_BFS to run optional method
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a
+ * sleep.
+ * Called with interrupts DISABLED.
+ *
+ ******************************************************************************/
+acpi_status acpi_leave_sleep_state_prep(u8 sleep_state, u8 flags)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep);
+
+ status =
+ acpi_hw_sleep_dispatch(sleep_state, flags,
+ ACPI_WAKE_PREP_FUNCTION_ID);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_leave_sleep_state_prep)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_leave_sleep_state
+ *
+ * PARAMETERS: sleep_state - Which sleep state we are exiting
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
+ * Called with interrupts ENABLED.
+ *
+ ******************************************************************************/
+acpi_status acpi_leave_sleep_state(u8 sleep_state)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_leave_sleep_state);
+
+
+ status = acpi_hw_sleep_dispatch(sleep_state, 0, ACPI_WAKE_FUNCTION_ID);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_leave_sleep_state)
diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c
index b7f2b3b..3f7f3f6 100644
--- a/drivers/acpi/acpica/nsdump.c
+++ b/drivers/acpi/acpica/nsdump.c
@@ -242,7 +242,20 @@ acpi_ns_dump_one_object(acpi_handle obj_handle,
if (!obj_desc) {
- /* No attached object, we are done */
+ /* No attached object. Some types should always have an object */
+
+ switch (type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_PACKAGE:
+ case ACPI_TYPE_BUFFER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_METHOD:
+ acpi_os_printf("<No attached object>");
+ break;
+
+ default:
+ break;
+ }
acpi_os_printf("\n");
return (AE_OK);
diff --git a/drivers/acpi/acpica/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c
index 30ea5bc..3b5acb0 100644
--- a/drivers/acpi/acpica/nsdumpdv.c
+++ b/drivers/acpi/acpica/nsdumpdv.c
@@ -121,7 +121,7 @@ void acpi_ns_dump_root_devices(void)
return;
}
- status = acpi_get_handle(NULL, ACPI_NS_SYSTEM_BUS, &sys_bus_handle);
+ status = acpi_get_handle(NULL, METHOD_NAME__SB_, &sys_bus_handle);
if (ACPI_FAILURE(status)) {
return;
}
diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c
index bbe46a4..23ce096 100644
--- a/drivers/acpi/acpica/nspredef.c
+++ b/drivers/acpi/acpica/nspredef.c
@@ -638,8 +638,8 @@ acpi_ns_check_package(struct acpi_predefined_data *data,
/* Create the new outer package and populate it */
status =
- acpi_ns_repair_package_list(data,
- return_object_ptr);
+ acpi_ns_wrap_with_package(data, *elements,
+ return_object_ptr);
if (ACPI_FAILURE(status)) {
return (status);
}
diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c
index 9c35d20..5519a64 100644
--- a/drivers/acpi/acpica/nsrepair.c
+++ b/drivers/acpi/acpica/nsrepair.c
@@ -71,11 +71,10 @@ ACPI_MODULE_NAME("nsrepair")
* Buffer -> String
* Buffer -> Package of Integers
* Package -> Package of one Package
+ * An incorrect standalone object is wrapped with required outer package
*
* Additional possible repairs:
- *
* Required package elements that are NULL replaced by Integer/String/Buffer
- * Incorrect standalone package wrapped with required outer package
*
******************************************************************************/
/* Local prototypes */
@@ -91,10 +90,6 @@ static acpi_status
acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
union acpi_operand_object **return_object);
-static acpi_status
-acpi_ns_convert_to_package(union acpi_operand_object *original_object,
- union acpi_operand_object **return_object);
-
/*******************************************************************************
*
* FUNCTION: acpi_ns_repair_object
@@ -151,9 +146,24 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
}
}
if (expected_btypes & ACPI_RTYPE_PACKAGE) {
- status = acpi_ns_convert_to_package(return_object, &new_object);
+ /*
+ * A package is expected. We will wrap the existing object with a
+ * new package object. It is often the case that if a variable-length
+ * package is required, but there is only a single object needed, the
+ * BIOS will return that object instead of wrapping it with a Package
+ * object. Note: after the wrapping, the package will be validated
+ * for correct contents (expected object type or types).
+ */
+ status =
+ acpi_ns_wrap_with_package(data, return_object, &new_object);
if (ACPI_SUCCESS(status)) {
- goto object_repaired;
+ /*
+ * The original object just had its reference count
+ * incremented for being inserted into the new package.
+ */
+ *return_object_ptr = new_object; /* New Package object */
+ data->flags |= ACPI_OBJECT_REPAIRED;
+ return (AE_OK);
}
}
@@ -165,22 +175,27 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
/* Object was successfully repaired */
- /*
- * If the original object is a package element, we need to:
- * 1. Set the reference count of the new object to match the
- * reference count of the old object.
- * 2. Decrement the reference count of the original object.
- */
if (package_index != ACPI_NOT_PACKAGE_ELEMENT) {
- new_object->common.reference_count =
- return_object->common.reference_count;
+ /*
+ * The original object is a package element. We need to
+ * decrement the reference count of the original object,
+ * for removing it from the package.
+ *
+ * However, if the original object was just wrapped with a
+ * package object as part of the repair, we don't need to
+ * change the reference count.
+ */
+ if (!(data->flags & ACPI_OBJECT_WRAPPED)) {
+ new_object->common.reference_count =
+ return_object->common.reference_count;
- if (return_object->common.reference_count > 1) {
- return_object->common.reference_count--;
+ if (return_object->common.reference_count > 1) {
+ return_object->common.reference_count--;
+ }
}
ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
- "%s: Converted %s to expected %s at index %u\n",
+ "%s: Converted %s to expected %s at Package index %u\n",
data->pathname,
acpi_ut_get_object_type_name(return_object),
acpi_ut_get_object_type_name(new_object),
@@ -453,65 +468,6 @@ acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
/*******************************************************************************
*
- * FUNCTION: acpi_ns_convert_to_package
- *
- * PARAMETERS: original_object - Object to be converted
- * return_object - Where the new converted object is returned
- *
- * RETURN: Status. AE_OK if conversion was successful.
- *
- * DESCRIPTION: Attempt to convert a Buffer object to a Package. Each byte of
- * the buffer is converted to a single integer package element.
- *
- ******************************************************************************/
-
-static acpi_status
-acpi_ns_convert_to_package(union acpi_operand_object *original_object,
- union acpi_operand_object **return_object)
-{
- union acpi_operand_object *new_object;
- union acpi_operand_object **elements;
- u32 length;
- u8 *buffer;
-
- switch (original_object->common.type) {
- case ACPI_TYPE_BUFFER:
-
- /* Buffer-to-Package conversion */
-
- length = original_object->buffer.length;
- new_object = acpi_ut_create_package_object(length);
- if (!new_object) {
- return (AE_NO_MEMORY);
- }
-
- /* Convert each buffer byte to an integer package element */
-
- elements = new_object->package.elements;
- buffer = original_object->buffer.pointer;
-
- while (length--) {
- *elements =
- acpi_ut_create_integer_object((u64) *buffer);
- if (!*elements) {
- acpi_ut_remove_reference(new_object);
- return (AE_NO_MEMORY);
- }
- elements++;
- buffer++;
- }
- break;
-
- default:
- return (AE_AML_OPERAND_TYPE);
- }
-
- *return_object = new_object;
- return (AE_OK);
-}
-
-/*******************************************************************************
- *
* FUNCTION: acpi_ns_repair_null_element
*
* PARAMETERS: Data - Pointer to validation data structure
@@ -677,55 +633,56 @@ acpi_ns_remove_null_elements(struct acpi_predefined_data *data,
/*******************************************************************************
*
- * FUNCTION: acpi_ns_repair_package_list
+ * FUNCTION: acpi_ns_wrap_with_package
*
* PARAMETERS: Data - Pointer to validation data structure
- * obj_desc_ptr - Pointer to the object to repair. The new
- * package object is returned here,
- * overwriting the old object.
+ * original_object - Pointer to the object to repair.
+ * obj_desc_ptr - The new package object is returned here
*
* RETURN: Status, new object in *obj_desc_ptr
*
- * DESCRIPTION: Repair a common problem with objects that are defined to return
- * a variable-length Package of Packages. If the variable-length
- * is one, some BIOS code mistakenly simply declares a single
- * Package instead of a Package with one sub-Package. This
- * function attempts to repair this error by wrapping a Package
- * object around the original Package, creating the correct
- * Package with one sub-Package.
+ * DESCRIPTION: Repair a common problem with objects that are defined to
+ * return a variable-length Package of sub-objects. If there is
+ * only one sub-object, some BIOS code mistakenly simply declares
+ * the single object instead of a Package with one sub-object.
+ * This function attempts to repair this error by wrapping a
+ * Package object around the original object, creating the
+ * correct and expected Package with one sub-object.
*
* Names that can be repaired in this manner include:
- * _ALR, _CSD, _HPX, _MLS, _PRT, _PSS, _TRT, TSS
+ * _ALR, _CSD, _HPX, _MLS, _PLD, _PRT, _PSS, _TRT, _TSS,
+ * _BCL, _DOD, _FIX, _Sx
*
******************************************************************************/
acpi_status
-acpi_ns_repair_package_list(struct acpi_predefined_data *data,
- union acpi_operand_object **obj_desc_ptr)
+acpi_ns_wrap_with_package(struct acpi_predefined_data *data,
+ union acpi_operand_object *original_object,
+ union acpi_operand_object **obj_desc_ptr)
{
union acpi_operand_object *pkg_obj_desc;
- ACPI_FUNCTION_NAME(ns_repair_package_list);
+ ACPI_FUNCTION_NAME(ns_wrap_with_package);
/*
* Create the new outer package and populate it. The new package will
- * have a single element, the lone subpackage.
+ * have a single element, the lone sub-object.
*/
pkg_obj_desc = acpi_ut_create_package_object(1);
if (!pkg_obj_desc) {
return (AE_NO_MEMORY);
}
- pkg_obj_desc->package.elements[0] = *obj_desc_ptr;
+ pkg_obj_desc->package.elements[0] = original_object;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Wrapped %s with expected Package object\n",
+ data->pathname,
+ acpi_ut_get_object_type_name(original_object)));
/* Return the new object in the object pointer */
*obj_desc_ptr = pkg_obj_desc;
- data->flags |= ACPI_OBJECT_REPAIRED;
-
- ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
- "%s: Repaired incorrectly formed Package\n",
- data->pathname));
-
+ data->flags |= ACPI_OBJECT_REPAIRED | ACPI_OBJECT_WRAPPED;
return (AE_OK);
}
diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c
index a535b7a..7511375 100644
--- a/drivers/acpi/acpica/nsutils.c
+++ b/drivers/acpi/acpica/nsutils.c
@@ -341,7 +341,7 @@ acpi_status acpi_ns_build_internal_name(struct acpi_namestring_info *info)
if (!acpi_ns_valid_path_separator(*external_name) &&
(*external_name != 0)) {
- return_ACPI_STATUS(AE_BAD_PARAMETER);
+ return_ACPI_STATUS(AE_BAD_PATHNAME);
}
/* Move on the next segment */
diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c
index c5d8704..4c9c760 100644
--- a/drivers/acpi/acpica/tbfadt.c
+++ b/drivers/acpi/acpica/tbfadt.c
@@ -363,10 +363,6 @@ static void acpi_tb_convert_fadt(void)
u32 address32;
u32 i;
- /* Update the local FADT table header length */
-
- acpi_gbl_FADT.header.length = sizeof(struct acpi_table_fadt);
-
/*
* Expand the 32-bit FACS and DSDT addresses to 64-bit as necessary.
* Later code will always use the X 64-bit field. Also, check for an
@@ -408,6 +404,10 @@ static void acpi_tb_convert_fadt(void)
acpi_gbl_FADT.boot_flags = 0;
}
+ /* Update the local FADT table header length */
+
+ acpi_gbl_FADT.header.length = sizeof(struct acpi_table_fadt);
+
/*
* Expand the ACPI 1.0 32-bit addresses to the ACPI 2.0 64-bit "X"
* generic address structures as necessary. Later code will always use
diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c
index 1aecf7b..c03500b 100644
--- a/drivers/acpi/acpica/tbinstal.c
+++ b/drivers/acpi/acpica/tbinstal.c
@@ -114,7 +114,6 @@ acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index)
{
u32 i;
acpi_status status = AE_OK;
- struct acpi_table_header *override_table = NULL;
ACPI_FUNCTION_TRACE(tb_add_table);
@@ -224,25 +223,10 @@ acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index)
/*
* ACPI Table Override:
* Allow the host to override dynamically loaded tables.
+ * NOTE: the table is fully mapped at this point, and the mapping will
+ * be deleted by tb_table_override if the table is actually overridden.
*/
- status = acpi_os_table_override(table_desc->pointer, &override_table);
- if (ACPI_SUCCESS(status) && override_table) {
- ACPI_INFO((AE_INFO,
- "%4.4s @ 0x%p Table override, replaced with:",
- table_desc->pointer->signature,
- ACPI_CAST_PTR(void, table_desc->address)));
-
- /* We can delete the table that was passed as a parameter */
-
- acpi_tb_delete_table(table_desc);
-
- /* Setup descriptor for the new table */
-
- table_desc->address = ACPI_PTR_TO_PHYSADDR(override_table);
- table_desc->pointer = override_table;
- table_desc->length = override_table->length;
- table_desc->flags = ACPI_TABLE_ORIGIN_OVERRIDE;
- }
+ (void)acpi_tb_table_override(table_desc->pointer, table_desc);
/* Add the table to the global root table list */
@@ -263,6 +247,95 @@ acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index)
/*******************************************************************************
*
+ * FUNCTION: acpi_tb_table_override
+ *
+ * PARAMETERS: table_header - Header for the original table
+ * table_desc - Table descriptor initialized for the
+ * original table. May or may not be mapped.
+ *
+ * RETURN: Pointer to the entire new table. NULL if table not overridden.
+ * If overridden, installs the new table within the input table
+ * descriptor.
+ *
+ * DESCRIPTION: Attempt table override by calling the OSL override functions.
+ * Note: If the table is overridden, then the entire new table
+ * is mapped and returned by this function.
+ *
+ ******************************************************************************/
+
+struct acpi_table_header *acpi_tb_table_override(struct acpi_table_header
+ *table_header,
+ struct acpi_table_desc
+ *table_desc)
+{
+ acpi_status status;
+ struct acpi_table_header *new_table = NULL;
+ acpi_physical_address new_address = 0;
+ u32 new_table_length = 0;
+ u8 new_flags;
+ char *override_type;
+
+ /* (1) Attempt logical override (returns a logical address) */
+
+ status = acpi_os_table_override(table_header, &new_table);
+ if (ACPI_SUCCESS(status) && new_table) {
+ new_address = ACPI_PTR_TO_PHYSADDR(new_table);
+ new_table_length = new_table->length;
+ new_flags = ACPI_TABLE_ORIGIN_OVERRIDE;
+ override_type = "Logical";
+ goto finish_override;
+ }
+
+ /* (2) Attempt physical override (returns a physical address) */
+
+ status = acpi_os_physical_table_override(table_header,
+ &new_address,
+ &new_table_length);
+ if (ACPI_SUCCESS(status) && new_address && new_table_length) {
+
+ /* Map the entire new table */
+
+ new_table = acpi_os_map_memory(new_address, new_table_length);
+ if (!new_table) {
+ ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY,
+ "%4.4s %p Attempted physical table override failed",
+ table_header->signature,
+ ACPI_CAST_PTR(void,
+ table_desc->address)));
+ return (NULL);
+ }
+
+ override_type = "Physical";
+ new_flags = ACPI_TABLE_ORIGIN_MAPPED;
+ goto finish_override;
+ }
+
+ return (NULL); /* There was no override */
+
+ finish_override:
+
+ ACPI_INFO((AE_INFO,
+ "%4.4s %p %s table override, new table: %p",
+ table_header->signature,
+ ACPI_CAST_PTR(void, table_desc->address),
+ override_type, new_table));
+
+ /* We can now unmap/delete the original table (if fully mapped) */
+
+ acpi_tb_delete_table(table_desc);
+
+ /* Setup descriptor for the new table */
+
+ table_desc->address = new_address;
+ table_desc->pointer = new_table;
+ table_desc->length = new_table_length;
+ table_desc->flags = new_flags;
+
+ return (new_table);
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_tb_resize_root_table_list
*
* PARAMETERS: None
@@ -396,7 +469,11 @@ void acpi_tb_delete_table(struct acpi_table_desc *table_desc)
case ACPI_TABLE_ORIGIN_ALLOCATED:
ACPI_FREE(table_desc->pointer);
break;
- default:;
+
+ /* Not mapped or allocated, there is nothing we can do */
+
+ default:
+ return;
}
table_desc->pointer = NULL;
diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c
index 09ca39e..0a706ca 100644
--- a/drivers/acpi/acpica/tbutils.c
+++ b/drivers/acpi/acpica/tbutils.c
@@ -118,6 +118,7 @@ acpi_tb_check_xsdt(acpi_physical_address address)
return AE_OK;
}
+#if (!ACPI_REDUCED_HARDWARE)
/*******************************************************************************
*
* FUNCTION: acpi_tb_initialize_facs
@@ -148,6 +149,7 @@ acpi_status acpi_tb_initialize_facs(void)
&acpi_gbl_FACS));
return status;
}
+#endif /* !ACPI_REDUCED_HARDWARE */
/*******************************************************************************
*
@@ -444,7 +446,7 @@ struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index)
* RETURN: None
*
* DESCRIPTION: Install an ACPI table into the global data structure. The
- * table override mechanism is implemented here to allow the host
+ * table override mechanism is called to allow the host
* OS to replace any table before it is installed in the root
* table array.
*
@@ -454,11 +456,9 @@ void
acpi_tb_install_table(acpi_physical_address address,
char *signature, u32 table_index)
{
- u8 flags;
- acpi_status status;
- struct acpi_table_header *table_to_install;
- struct acpi_table_header *mapped_table;
- struct acpi_table_header *override_table = NULL;
+ struct acpi_table_header *table;
+ struct acpi_table_header *final_table;
+ struct acpi_table_desc *table_desc;
if (!address) {
ACPI_ERROR((AE_INFO,
@@ -469,69 +469,78 @@ acpi_tb_install_table(acpi_physical_address address,
/* Map just the table header */
- mapped_table =
- acpi_os_map_memory(address, sizeof(struct acpi_table_header));
- if (!mapped_table) {
+ table = acpi_os_map_memory(address, sizeof(struct acpi_table_header));
+ if (!table) {
+ ACPI_ERROR((AE_INFO,
+ "Could not map memory for table [%s] at %p",
+ signature, ACPI_CAST_PTR(void, address)));
return;
}
/* If a particular signature is expected (DSDT/FACS), it must match */
- if (signature && !ACPI_COMPARE_NAME(mapped_table->signature, signature)) {
+ if (signature && !ACPI_COMPARE_NAME(table->signature, signature)) {
ACPI_ERROR((AE_INFO,
"Invalid signature 0x%X for ACPI table, expected [%s]",
- *ACPI_CAST_PTR(u32, mapped_table->signature),
- signature));
+ *ACPI_CAST_PTR(u32, table->signature), signature));
goto unmap_and_exit;
}
/*
+ * Initialize the table entry. Set the pointer to NULL, since the
+ * table is not fully mapped at this time.
+ */
+ table_desc = &acpi_gbl_root_table_list.tables[table_index];
+
+ table_desc->address = address;
+ table_desc->pointer = NULL;
+ table_desc->length = table->length;
+ table_desc->flags = ACPI_TABLE_ORIGIN_MAPPED;
+ ACPI_MOVE_32_TO_32(table_desc->signature.ascii, table->signature);
+
+ /*
* ACPI Table Override:
*
* Before we install the table, let the host OS override it with a new
* one if desired. Any table within the RSDT/XSDT can be replaced,
* including the DSDT which is pointed to by the FADT.
+ *
+ * NOTE: If the table is overridden, then final_table will contain a
+ * mapped pointer to the full new table. If the table is not overridden,
+ * or if there has been a physical override, then the table will be
+ * fully mapped later (in verify table). In any case, we must
+ * unmap the header that was mapped above.
*/
- status = acpi_os_table_override(mapped_table, &override_table);
- if (ACPI_SUCCESS(status) && override_table) {
- ACPI_INFO((AE_INFO,
- "%4.4s @ 0x%p Table override, replaced with:",
- mapped_table->signature, ACPI_CAST_PTR(void,
- address)));
-
- acpi_gbl_root_table_list.tables[table_index].pointer =
- override_table;
- address = ACPI_PTR_TO_PHYSADDR(override_table);
-
- table_to_install = override_table;
- flags = ACPI_TABLE_ORIGIN_OVERRIDE;
- } else {
- table_to_install = mapped_table;
- flags = ACPI_TABLE_ORIGIN_MAPPED;
+ final_table = acpi_tb_table_override(table, table_desc);
+ if (!final_table) {
+ final_table = table; /* There was no override */
}
- /* Initialize the table entry */
+ acpi_tb_print_table_header(table_desc->address, final_table);
- acpi_gbl_root_table_list.tables[table_index].address = address;
- acpi_gbl_root_table_list.tables[table_index].length =
- table_to_install->length;
- acpi_gbl_root_table_list.tables[table_index].flags = flags;
-
- ACPI_MOVE_32_TO_32(&
- (acpi_gbl_root_table_list.tables[table_index].
- signature), table_to_install->signature);
-
- acpi_tb_print_table_header(address, table_to_install);
+ /* Set the global integer width (based upon revision of the DSDT) */
if (table_index == ACPI_TABLE_INDEX_DSDT) {
+ acpi_ut_set_integer_width(final_table->revision);
+ }
- /* Global integer width is based upon revision of the DSDT */
-
- acpi_ut_set_integer_width(table_to_install->revision);
+ /*
+ * If we have a physical override during this early loading of the ACPI
+ * tables, unmap the table for now. It will be mapped again later when
+ * it is actually used. This supports very early loading of ACPI tables,
+ * before virtual memory is fully initialized and running within the
+ * host OS. Note: A logical override has the ACPI_TABLE_ORIGIN_OVERRIDE
+ * flag set and will not be deleted below.
+ */
+ if (final_table != table) {
+ acpi_tb_delete_table(table_desc);
}
unmap_and_exit:
- acpi_os_unmap_memory(mapped_table, sizeof(struct acpi_table_header));
+
+ /* Always unmap the table header that we mapped above */
+
+ acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
}
/*******************************************************************************
diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c
index d42ede52..6848499 100644
--- a/drivers/acpi/acpica/utdecode.c
+++ b/drivers/acpi/acpica/utdecode.c
@@ -497,19 +497,20 @@ char *acpi_ut_get_mutex_name(u32 mutex_id)
/* Names for Notify() values, used for debug output */
-static const char *acpi_gbl_notify_value_names[] = {
- "Bus Check",
- "Device Check",
- "Device Wake",
- "Eject Request",
- "Device Check Light",
- "Frequency Mismatch",
- "Bus Mode Mismatch",
- "Power Fault",
- "Capabilities Check",
- "Device PLD Check",
- "Reserved",
- "System Locality Update"
+static const char *acpi_gbl_notify_value_names[ACPI_NOTIFY_MAX + 1] = {
+ /* 00 */ "Bus Check",
+ /* 01 */ "Device Check",
+ /* 02 */ "Device Wake",
+ /* 03 */ "Eject Request",
+ /* 04 */ "Device Check Light",
+ /* 05 */ "Frequency Mismatch",
+ /* 06 */ "Bus Mode Mismatch",
+ /* 07 */ "Power Fault",
+ /* 08 */ "Capabilities Check",
+ /* 09 */ "Device PLD Check",
+ /* 10 */ "Reserved",
+ /* 11 */ "System Locality Update",
+ /* 12 */ "Shutdown Request"
};
const char *acpi_ut_get_notify_name(u32 notify_value)
@@ -519,9 +520,10 @@ const char *acpi_ut_get_notify_name(u32 notify_value)
return (acpi_gbl_notify_value_names[notify_value]);
} else if (notify_value <= ACPI_MAX_SYS_NOTIFY) {
return ("Reserved");
- } else { /* Greater or equal to 0x80 */
-
- return ("**Device Specific**");
+ } else if (notify_value <= ACPI_MAX_DEVICE_SPECIFIC_NOTIFY) {
+ return ("Device Specific");
+ } else {
+ return ("Hardware Specific");
}
}
#endif
diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c
index 4153584..90f53b4 100644
--- a/drivers/acpi/acpica/utglobal.c
+++ b/drivers/acpi/acpica/utglobal.c
@@ -140,6 +140,7 @@ const struct acpi_predefined_names acpi_gbl_pre_defined_names[] = {
{NULL, ACPI_TYPE_ANY, NULL}
};
+#if (!ACPI_REDUCED_HARDWARE)
/******************************************************************************
*
* Event and Hardware globals
@@ -236,6 +237,7 @@ struct acpi_fixed_event_info acpi_gbl_fixed_event_info[ACPI_NUM_FIXED_EVENTS] =
ACPI_BITMASK_RT_CLOCK_STATUS,
ACPI_BITMASK_RT_CLOCK_ENABLE},
};
+#endif /* !ACPI_REDUCED_HARDWARE */
/*******************************************************************************
*
@@ -286,6 +288,8 @@ acpi_status acpi_ut_init_globals(void)
acpi_gbl_owner_id_mask[ACPI_NUM_OWNERID_MASKS - 1] = 0x80000000;
+#if (!ACPI_REDUCED_HARDWARE)
+
/* GPE support */
acpi_gbl_gpe_xrupt_list_head = NULL;
@@ -294,6 +298,10 @@ acpi_status acpi_ut_init_globals(void)
acpi_current_gpe_count = 0;
acpi_gbl_all_gpes_initialized = FALSE;
+ acpi_gbl_global_event_handler = NULL;
+
+#endif /* !ACPI_REDUCED_HARDWARE */
+
/* Global handlers */
acpi_gbl_system_notify.handler = NULL;
@@ -302,7 +310,6 @@ acpi_status acpi_ut_init_globals(void)
acpi_gbl_init_handler = NULL;
acpi_gbl_table_handler = NULL;
acpi_gbl_interface_handler = NULL;
- acpi_gbl_global_event_handler = NULL;
/* Global Lock support */
diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c
index 8359c0c..246798e 100644
--- a/drivers/acpi/acpica/utinit.c
+++ b/drivers/acpi/acpica/utinit.c
@@ -53,27 +53,35 @@ ACPI_MODULE_NAME("utinit")
/* Local prototypes */
static void acpi_ut_terminate(void);
+#if (!ACPI_REDUCED_HARDWARE)
+
+static void acpi_ut_free_gpe_lists(void);
+
+#else
+
+#define acpi_ut_free_gpe_lists()
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+#if (!ACPI_REDUCED_HARDWARE)
/******************************************************************************
*
- * FUNCTION: acpi_ut_terminate
+ * FUNCTION: acpi_ut_free_gpe_lists
*
* PARAMETERS: none
*
* RETURN: none
*
- * DESCRIPTION: Free global memory
+ * DESCRIPTION: Free global GPE lists
*
******************************************************************************/
-static void acpi_ut_terminate(void)
+static void acpi_ut_free_gpe_lists(void)
{
struct acpi_gpe_block_info *gpe_block;
struct acpi_gpe_block_info *next_gpe_block;
struct acpi_gpe_xrupt_info *gpe_xrupt_info;
struct acpi_gpe_xrupt_info *next_gpe_xrupt_info;
- ACPI_FUNCTION_TRACE(ut_terminate);
-
/* Free global GPE blocks and related info structures */
gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head;
@@ -91,7 +99,26 @@ static void acpi_ut_terminate(void)
ACPI_FREE(gpe_xrupt_info);
gpe_xrupt_info = next_gpe_xrupt_info;
}
+}
+#endif /* !ACPI_REDUCED_HARDWARE */
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ut_terminate
+ *
+ * PARAMETERS: none
+ *
+ * RETURN: none
+ *
+ * DESCRIPTION: Free global memory
+ *
+ ******************************************************************************/
+
+static void acpi_ut_terminate(void)
+{
+ ACPI_FUNCTION_TRACE(ut_terminate);
+ acpi_ut_free_gpe_lists();
acpi_ut_delete_address_lists();
return_VOID;
}
diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c
index 644e8c8..afa94f5 100644
--- a/drivers/acpi/acpica/utxface.c
+++ b/drivers/acpi/acpica/utxface.c
@@ -145,6 +145,8 @@ acpi_status acpi_enable_subsystem(u32 flags)
ACPI_FUNCTION_TRACE(acpi_enable_subsystem);
+#if (!ACPI_REDUCED_HARDWARE)
+
/* Enable ACPI mode */
if (!(flags & ACPI_NO_ACPI_ENABLE)) {
@@ -169,6 +171,7 @@ acpi_status acpi_enable_subsystem(u32 flags)
ACPI_WARNING((AE_INFO, "Could not map the FACS table"));
return_ACPI_STATUS(status);
}
+#endif /* !ACPI_REDUCED_HARDWARE */
/*
* Install the default op_region handlers. These are installed unless
@@ -184,7 +187,7 @@ acpi_status acpi_enable_subsystem(u32 flags)
return_ACPI_STATUS(status);
}
}
-
+#if (!ACPI_REDUCED_HARDWARE)
/*
* Initialize ACPI Event handling (Fixed and General Purpose)
*
@@ -220,6 +223,7 @@ acpi_status acpi_enable_subsystem(u32 flags)
return_ACPI_STATUS(status);
}
}
+#endif /* !ACPI_REDUCED_HARDWARE */
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c
index e5d53b7..5577762 100644
--- a/drivers/acpi/apei/apei-base.c
+++ b/drivers/acpi/apei/apei-base.c
@@ -558,33 +558,48 @@ void apei_resources_release(struct apei_resources *resources)
}
EXPORT_SYMBOL_GPL(apei_resources_release);
-static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr)
+static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr,
+ u32 *access_bit_width)
{
- u32 width, space_id;
+ u32 bit_width, bit_offset, access_size_code, space_id;
- width = reg->bit_width;
+ bit_width = reg->bit_width;
+ bit_offset = reg->bit_offset;
+ access_size_code = reg->access_width;
space_id = reg->space_id;
/* Handle possible alignment issues */
memcpy(paddr, &reg->address, sizeof(*paddr));
if (!*paddr) {
pr_warning(FW_BUG APEI_PFX
- "Invalid physical address in GAR [0x%llx/%u/%u]\n",
- *paddr, width, space_id);
+ "Invalid physical address in GAR [0x%llx/%u/%u/%u/%u]\n",
+ *paddr, bit_width, bit_offset, access_size_code,
+ space_id);
return -EINVAL;
}
- if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) {
+ if (access_size_code < 1 || access_size_code > 4) {
pr_warning(FW_BUG APEI_PFX
- "Invalid bit width in GAR [0x%llx/%u/%u]\n",
- *paddr, width, space_id);
+ "Invalid access size code in GAR [0x%llx/%u/%u/%u/%u]\n",
+ *paddr, bit_width, bit_offset, access_size_code,
+ space_id);
+ return -EINVAL;
+ }
+ *access_bit_width = 1UL << (access_size_code + 2);
+
+ if ((bit_width + bit_offset) > *access_bit_width) {
+ pr_warning(FW_BUG APEI_PFX
+ "Invalid bit width + offset in GAR [0x%llx/%u/%u/%u/%u]\n",
+ *paddr, bit_width, bit_offset, access_size_code,
+ space_id);
return -EINVAL;
}
if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
pr_warning(FW_BUG APEI_PFX
- "Invalid address space type in GAR [0x%llx/%u/%u]\n",
- *paddr, width, space_id);
+ "Invalid address space type in GAR [0x%llx/%u/%u/%u/%u]\n",
+ *paddr, bit_width, bit_offset, access_size_code,
+ space_id);
return -EINVAL;
}
@@ -595,23 +610,25 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr)
int apei_read(u64 *val, struct acpi_generic_address *reg)
{
int rc;
+ u32 access_bit_width;
u64 address;
acpi_status status;
- rc = apei_check_gar(reg, &address);
+ rc = apei_check_gar(reg, &address, &access_bit_width);
if (rc)
return rc;
*val = 0;
switch(reg->space_id) {
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
- status = acpi_os_read_memory64((acpi_physical_address)
- address, val, reg->bit_width);
+ status = acpi_os_read_memory((acpi_physical_address) address,
+ val, access_bit_width);
if (ACPI_FAILURE(status))
return -EIO;
break;
case ACPI_ADR_SPACE_SYSTEM_IO:
- status = acpi_os_read_port(address, (u32 *)val, reg->bit_width);
+ status = acpi_os_read_port(address, (u32 *)val,
+ access_bit_width);
if (ACPI_FAILURE(status))
return -EIO;
break;
@@ -627,22 +644,23 @@ EXPORT_SYMBOL_GPL(apei_read);
int apei_write(u64 val, struct acpi_generic_address *reg)
{
int rc;
+ u32 access_bit_width;
u64 address;
acpi_status status;
- rc = apei_check_gar(reg, &address);
+ rc = apei_check_gar(reg, &address, &access_bit_width);
if (rc)
return rc;
switch (reg->space_id) {
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
- status = acpi_os_write_memory64((acpi_physical_address)
- address, val, reg->bit_width);
+ status = acpi_os_write_memory((acpi_physical_address) address,
+ val, access_bit_width);
if (ACPI_FAILURE(status))
return -EIO;
break;
case ACPI_ADR_SPACE_SYSTEM_IO:
- status = acpi_os_write_port(address, val, reg->bit_width);
+ status = acpi_os_write_port(address, val, access_bit_width);
if (ACPI_FAILURE(status))
return -EIO;
break;
@@ -661,23 +679,24 @@ static int collect_res_callback(struct apei_exec_context *ctx,
struct apei_resources *resources = data;
struct acpi_generic_address *reg = &entry->register_region;
u8 ins = entry->instruction;
+ u32 access_bit_width;
u64 paddr;
int rc;
if (!(ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER))
return 0;
- rc = apei_check_gar(reg, &paddr);
+ rc = apei_check_gar(reg, &paddr, &access_bit_width);
if (rc)
return rc;
switch (reg->space_id) {
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
return apei_res_add(&resources->iomem, paddr,
- reg->bit_width / 8);
+ access_bit_width / 8);
case ACPI_ADR_SPACE_SYSTEM_IO:
return apei_res_add(&resources->ioport, paddr,
- reg->bit_width / 8);
+ access_bit_width / 8);
default:
return -EINVAL;
}
diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c
index 5d41894..e6defd8 100644
--- a/drivers/acpi/apei/cper.c
+++ b/drivers/acpi/apei/cper.c
@@ -362,6 +362,7 @@ void apei_estatus_print(const char *pfx,
gedata_len = gdata->error_data_length;
apei_estatus_print_section(pfx, gdata, sec_no);
data_len -= gedata_len + sizeof(*gdata);
+ gdata = (void *)(gdata + 1) + gedata_len;
sec_no++;
}
}
@@ -396,6 +397,7 @@ int apei_estatus_check(const struct acpi_hest_generic_status *estatus)
if (gedata_len > data_len - sizeof(*gdata))
return -EINVAL;
data_len -= gedata_len + sizeof(*gdata);
+ gdata = (void *)(gdata + 1) + gedata_len;
}
if (data_len)
return -EINVAL;
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c
index 4ca087d..8e17936 100644
--- a/drivers/acpi/apei/einj.c
+++ b/drivers/acpi/apei/einj.c
@@ -74,6 +74,8 @@ struct vendor_error_type_extension {
u8 reserved[3];
};
+static u32 notrigger;
+
static u32 vendor_flags;
static struct debugfs_blob_wrapper vendor_blob;
static char vendor_dev[64];
@@ -238,7 +240,7 @@ static void *einj_get_parameter_address(void)
return v5param;
}
}
- if (paddrv4) {
+ if (param_extension && paddrv4) {
struct einj_parameter *v4param;
v4param = acpi_os_map_memory(paddrv4, sizeof(*v4param));
@@ -496,9 +498,11 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
if (rc)
return rc;
trigger_paddr = apei_exec_ctx_get_output(&ctx);
- rc = __einj_error_trigger(trigger_paddr, type, param1, param2);
- if (rc)
- return rc;
+ if (notrigger == 0) {
+ rc = __einj_error_trigger(trigger_paddr, type, param1, param2);
+ if (rc)
+ return rc;
+ }
rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION);
return rc;
@@ -700,6 +704,11 @@ static int __init einj_init(void)
einj_debug_dir, &error_param2);
if (!fentry)
goto err_unmap;
+
+ fentry = debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR,
+ einj_debug_dir, &notrigger);
+ if (!fentry)
+ goto err_unmap;
}
if (vendor_dev[0]) {
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index eb9fab5..e4d9d24 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -917,7 +917,7 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)
{
if ((erst_tab->header_length !=
(sizeof(struct acpi_table_erst) - sizeof(erst_tab->header)))
- && (erst_tab->header_length != sizeof(struct acpi_table_einj)))
+ && (erst_tab->header_length != sizeof(struct acpi_table_erst)))
return -EINVAL;
if (erst_tab->header.length < sizeof(struct acpi_table_erst))
return -EINVAL;
diff --git a/drivers/acpi/bgrt.c b/drivers/acpi/bgrt.c
new file mode 100644
index 0000000..8cf6c46
--- /dev/null
+++ b/drivers/acpi/bgrt.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2012 Red Hat, Inc <mjg@redhat.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/init.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+
+static struct acpi_table_bgrt *bgrt_tab;
+static struct kobject *bgrt_kobj;
+
+struct bmp_header {
+ u16 id;
+ u32 size;
+} __attribute ((packed));
+
+static struct bmp_header bmp_header;
+
+static ssize_t show_version(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->version);
+}
+static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
+
+static ssize_t show_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->status);
+}
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+
+static ssize_t show_type(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_type);
+}
+static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
+
+static ssize_t show_xoffset(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_offset_x);
+}
+static DEVICE_ATTR(xoffset, S_IRUGO, show_xoffset, NULL);
+
+static ssize_t show_yoffset(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_offset_y);
+}
+static DEVICE_ATTR(yoffset, S_IRUGO, show_yoffset, NULL);
+
+static ssize_t show_image(struct file *file, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off, size_t count)
+{
+ int size = attr->size;
+ void __iomem *image = attr->private;
+
+ if (off >= size) {
+ count = 0;
+ } else {
+ if (off + count > size)
+ count = size - off;
+
+ memcpy_fromio(buf, image+off, count);
+ }
+
+ return count;
+}
+
+static struct bin_attribute image_attr = {
+ .attr = {
+ .name = "image",
+ .mode = S_IRUGO,
+ },
+ .read = show_image,
+};
+
+static struct attribute *bgrt_attributes[] = {
+ &dev_attr_version.attr,
+ &dev_attr_status.attr,
+ &dev_attr_type.attr,
+ &dev_attr_xoffset.attr,
+ &dev_attr_yoffset.attr,
+ NULL,
+};
+
+static struct attribute_group bgrt_attribute_group = {
+ .attrs = bgrt_attributes,
+};
+
+static int __init bgrt_init(void)
+{
+ acpi_status status;
+ int ret;
+ void __iomem *bgrt;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ status = acpi_get_table("BGRT", 0,
+ (struct acpi_table_header **)&bgrt_tab);
+
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ sysfs_bin_attr_init(&image_attr);
+
+ bgrt = ioremap(bgrt_tab->image_address, sizeof(struct bmp_header));
+
+ if (!bgrt) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ memcpy_fromio(&bmp_header, bgrt, sizeof(bmp_header));
+ image_attr.size = bmp_header.size;
+ iounmap(bgrt);
+
+ image_attr.private = ioremap(bgrt_tab->image_address, image_attr.size);
+
+ if (!image_attr.private) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+
+ bgrt_kobj = kobject_create_and_add("bgrt", acpi_kobj);
+ if (!bgrt_kobj) {
+ ret = -EINVAL;
+ goto out_iounmap;
+ }
+
+ ret = sysfs_create_group(bgrt_kobj, &bgrt_attribute_group);
+ if (ret)
+ goto out_kobject;
+
+ ret = sysfs_create_bin_file(bgrt_kobj, &image_attr);
+ if (ret)
+ goto out_group;
+
+ return 0;
+
+out_group:
+ sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group);
+out_kobject:
+ kobject_put(bgrt_kobj);
+out_iounmap:
+ iounmap(image_attr.private);
+out_err:
+ return ret;
+}
+
+static void __exit bgrt_exit(void)
+{
+ iounmap(image_attr.private);
+ sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group);
+ sysfs_remove_bin_file(bgrt_kobj, &image_attr);
+}
+
+module_init(bgrt_init);
+module_exit(bgrt_exit);
+
+MODULE_AUTHOR("Matthew Garrett");
+MODULE_DESCRIPTION("BGRT boot graphic support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 9ecec98..3263b68 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -1010,6 +1010,7 @@ static int __init acpi_bus_init(void)
}
struct kobject *acpi_kobj;
+EXPORT_SYMBOL_GPL(acpi_kobj);
static int __init acpi_init(void)
{
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index b19a18d..7edaccc 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -445,6 +445,16 @@ int ec_transaction(u8 command,
EXPORT_SYMBOL(ec_transaction);
+/* Get the handle to the EC device */
+acpi_handle ec_get_handle(void)
+{
+ if (!first_ec)
+ return NULL;
+ return first_ec->handle;
+}
+
+EXPORT_SYMBOL(ec_get_handle);
+
void acpi_ec_block_transactions(void)
{
struct acpi_ec *ec = first_ec;
@@ -812,10 +822,10 @@ static int acpi_ec_add(struct acpi_device *device)
first_ec = ec;
device->driver_data = ec;
- WARN(!request_region(ec->data_addr, 1, "EC data"),
- "Could not request EC data io port 0x%lx", ec->data_addr);
- WARN(!request_region(ec->command_addr, 1, "EC cmd"),
- "Could not request EC cmd io port 0x%lx", ec->command_addr);
+ ret = !!request_region(ec->data_addr, 1, "EC data");
+ WARN(!ret, "Could not request EC data io port 0x%lx", ec->data_addr);
+ ret = !!request_region(ec->command_addr, 1, "EC cmd");
+ WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr);
pr_info(PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n",
ec->gpe, ec->command_addr, ec->data_addr);
diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c
index 7a2035f..266bc58 100644
--- a/drivers/acpi/nvs.c
+++ b/drivers/acpi/nvs.c
@@ -95,8 +95,8 @@ static int suspend_nvs_register(unsigned long start, unsigned long size)
{
struct nvs_page *entry, *next;
- pr_info("PM: Registering ACPI NVS region at %lx (%ld bytes)\n",
- start, size);
+ pr_info("PM: Registering ACPI NVS region [mem %#010lx-%#010lx] (%ld bytes)\n",
+ start, start + size - 1, size);
while (size > 0) {
unsigned int nr_bytes;
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 412a1e0..ba14fb9 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -77,6 +77,9 @@ EXPORT_SYMBOL(acpi_in_debugger);
extern char line_buf[80];
#endif /*ENABLE_DEBUGGER */
+static int (*__acpi_os_prepare_sleep)(u8 sleep_state, u32 pm1a_ctrl,
+ u32 pm1b_ctrl);
+
static acpi_osd_handler acpi_irq_handler;
static void *acpi_irq_context;
static struct workqueue_struct *kacpid_wq;
@@ -347,7 +350,7 @@ static void acpi_unmap(acpi_physical_address pg_off, void __iomem *vaddr)
unsigned long pfn;
pfn = pg_off >> PAGE_SHIFT;
- if (page_is_ram(pfn))
+ if (should_use_kmap(pfn))
kunmap(pfn_to_page(pfn));
else
iounmap(vaddr);
@@ -554,6 +557,15 @@ acpi_os_table_override(struct acpi_table_header * existing_table,
return AE_OK;
}
+acpi_status
+acpi_os_physical_table_override(struct acpi_table_header *existing_table,
+ acpi_physical_address * new_address,
+ u32 *new_table_length)
+{
+ return AE_SUPPORT;
+}
+
+
static irqreturn_t acpi_irq(int irq, void *dev_id)
{
u32 handled;
@@ -595,7 +607,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
acpi_irq_handler = handler;
acpi_irq_context = context;
- if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) {
+ if (request_threaded_irq(irq, NULL, acpi_irq, IRQF_SHARED, "acpi",
+ acpi_irq)) {
printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq);
acpi_irq_handler = NULL;
return AE_NOT_ACQUIRED;
@@ -699,49 +712,6 @@ acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width)
EXPORT_SYMBOL(acpi_os_write_port);
-acpi_status
-acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width)
-{
- void __iomem *virt_addr;
- unsigned int size = width / 8;
- bool unmap = false;
- u32 dummy;
-
- rcu_read_lock();
- virt_addr = acpi_map_vaddr_lookup(phys_addr, size);
- if (!virt_addr) {
- rcu_read_unlock();
- virt_addr = acpi_os_ioremap(phys_addr, size);
- if (!virt_addr)
- return AE_BAD_ADDRESS;
- unmap = true;
- }
-
- if (!value)
- value = &dummy;
-
- switch (width) {
- case 8:
- *(u8 *) value = readb(virt_addr);
- break;
- case 16:
- *(u16 *) value = readw(virt_addr);
- break;
- case 32:
- *(u32 *) value = readl(virt_addr);
- break;
- default:
- BUG();
- }
-
- if (unmap)
- iounmap(virt_addr);
- else
- rcu_read_unlock();
-
- return AE_OK;
-}
-
#ifdef readq
static inline u64 read64(const volatile void __iomem *addr)
{
@@ -758,7 +728,7 @@ static inline u64 read64(const volatile void __iomem *addr)
#endif
acpi_status
-acpi_os_read_memory64(acpi_physical_address phys_addr, u64 *value, u32 width)
+acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width)
{
void __iomem *virt_addr;
unsigned int size = width / 8;
@@ -803,45 +773,6 @@ acpi_os_read_memory64(acpi_physical_address phys_addr, u64 *value, u32 width)
return AE_OK;
}
-acpi_status
-acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width)
-{
- void __iomem *virt_addr;
- unsigned int size = width / 8;
- bool unmap = false;
-
- rcu_read_lock();
- virt_addr = acpi_map_vaddr_lookup(phys_addr, size);
- if (!virt_addr) {
- rcu_read_unlock();
- virt_addr = acpi_os_ioremap(phys_addr, size);
- if (!virt_addr)
- return AE_BAD_ADDRESS;
- unmap = true;
- }
-
- switch (width) {
- case 8:
- writeb(value, virt_addr);
- break;
- case 16:
- writew(value, virt_addr);
- break;
- case 32:
- writel(value, virt_addr);
- break;
- default:
- BUG();
- }
-
- if (unmap)
- iounmap(virt_addr);
- else
- rcu_read_unlock();
-
- return AE_OK;
-}
-
#ifdef writeq
static inline void write64(u64 val, volatile void __iomem *addr)
{
@@ -856,7 +787,7 @@ static inline void write64(u64 val, volatile void __iomem *addr)
#endif
acpi_status
-acpi_os_write_memory64(acpi_physical_address phys_addr, u64 value, u32 width)
+acpi_os_write_memory(acpi_physical_address phys_addr, u64 value, u32 width)
{
void __iomem *virt_addr;
unsigned int size = width / 8;
@@ -1641,3 +1572,24 @@ acpi_status acpi_os_terminate(void)
return AE_OK;
}
+
+acpi_status acpi_os_prepare_sleep(u8 sleep_state, u32 pm1a_control,
+ u32 pm1b_control)
+{
+ int rc = 0;
+ if (__acpi_os_prepare_sleep)
+ rc = __acpi_os_prepare_sleep(sleep_state,
+ pm1a_control, pm1b_control);
+ if (rc < 0)
+ return AE_ERROR;
+ else if (rc > 0)
+ return AE_CTRL_SKIP;
+
+ return AE_OK;
+}
+
+void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state,
+ u32 pm1a_ctrl, u32 pm1b_ctrl))
+{
+ __acpi_os_prepare_sleep = func;
+}
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 9ac2a9f..7049a7d 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -40,9 +40,11 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
+#include <linux/pm_runtime.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#include "sleep.h"
+#include "internal.h"
#define PREFIX "ACPI: "
@@ -77,6 +79,20 @@ static struct acpi_driver acpi_power_driver = {
},
};
+/*
+ * A power managed device
+ * A device may rely on multiple power resources.
+ * */
+struct acpi_power_managed_device {
+ struct device *dev; /* The physical device */
+ acpi_handle *handle;
+};
+
+struct acpi_power_resource_device {
+ struct acpi_power_managed_device *device;
+ struct acpi_power_resource_device *next;
+};
+
struct acpi_power_resource {
struct acpi_device * device;
acpi_bus_id name;
@@ -84,6 +100,9 @@ struct acpi_power_resource {
u32 order;
unsigned int ref_count;
struct mutex resource_lock;
+
+ /* List of devices relying on this power resource */
+ struct acpi_power_resource_device *devices;
};
static struct list_head acpi_power_resource_list;
@@ -183,8 +202,26 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
return 0;
}
+/* Resume the device when all power resources in _PR0 are on */
+static void acpi_power_on_device(struct acpi_power_managed_device *device)
+{
+ struct acpi_device *acpi_dev;
+ acpi_handle handle = device->handle;
+ int state;
+
+ if (acpi_bus_get_device(handle, &acpi_dev))
+ return;
+
+ if(acpi_power_get_inferred_state(acpi_dev, &state))
+ return;
+
+ if (state == ACPI_STATE_D0 && pm_runtime_suspended(device->dev))
+ pm_request_resume(device->dev);
+}
+
static int __acpi_power_on(struct acpi_power_resource *resource)
{
+ struct acpi_power_resource_device *device_list = resource->devices;
acpi_status status = AE_OK;
status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
@@ -197,6 +234,12 @@ static int __acpi_power_on(struct acpi_power_resource *resource)
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n",
resource->name));
+ while (device_list) {
+ acpi_power_on_device(device_list->device);
+
+ device_list = device_list->next;
+ }
+
return 0;
}
@@ -299,6 +342,125 @@ static int acpi_power_on_list(struct acpi_handle_list *list)
return result;
}
+static void __acpi_power_resource_unregister_device(struct device *dev,
+ acpi_handle res_handle)
+{
+ struct acpi_power_resource *resource = NULL;
+ struct acpi_power_resource_device *prev, *curr;
+
+ if (acpi_power_get_context(res_handle, &resource))
+ return;
+
+ mutex_lock(&resource->resource_lock);
+ prev = NULL;
+ curr = resource->devices;
+ while (curr) {
+ if (curr->device->dev == dev) {
+ if (!prev)
+ resource->devices = curr->next;
+ else
+ prev->next = curr->next;
+
+ kfree(curr);
+ break;
+ }
+
+ prev = curr;
+ curr = curr->next;
+ }
+ mutex_unlock(&resource->resource_lock);
+}
+
+/* Unlink dev from all power resources in _PR0 */
+void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle)
+{
+ struct acpi_device *acpi_dev;
+ struct acpi_handle_list *list;
+ int i;
+
+ if (!dev || !handle)
+ return;
+
+ if (acpi_bus_get_device(handle, &acpi_dev))
+ return;
+
+ list = &acpi_dev->power.states[ACPI_STATE_D0].resources;
+
+ for (i = 0; i < list->count; i++)
+ __acpi_power_resource_unregister_device(dev,
+ list->handles[i]);
+}
+
+static int __acpi_power_resource_register_device(
+ struct acpi_power_managed_device *powered_device, acpi_handle handle)
+{
+ struct acpi_power_resource *resource = NULL;
+ struct acpi_power_resource_device *power_resource_device;
+ int result;
+
+ result = acpi_power_get_context(handle, &resource);
+ if (result)
+ return result;
+
+ power_resource_device = kzalloc(
+ sizeof(*power_resource_device), GFP_KERNEL);
+ if (!power_resource_device)
+ return -ENOMEM;
+
+ power_resource_device->device = powered_device;
+
+ mutex_lock(&resource->resource_lock);
+ power_resource_device->next = resource->devices;
+ resource->devices = power_resource_device;
+ mutex_unlock(&resource->resource_lock);
+
+ return 0;
+}
+
+/* Link dev to all power resources in _PR0 */
+int acpi_power_resource_register_device(struct device *dev, acpi_handle handle)
+{
+ struct acpi_device *acpi_dev;
+ struct acpi_handle_list *list;
+ struct acpi_power_managed_device *powered_device;
+ int i, ret;
+
+ if (!dev || !handle)
+ return -ENODEV;
+
+ ret = acpi_bus_get_device(handle, &acpi_dev);
+ if (ret)
+ goto no_power_resource;
+
+ if (!acpi_dev->power.flags.power_resources)
+ goto no_power_resource;
+
+ powered_device = kzalloc(sizeof(*powered_device), GFP_KERNEL);
+ if (!powered_device)
+ return -ENOMEM;
+
+ powered_device->dev = dev;
+ powered_device->handle = handle;
+
+ list = &acpi_dev->power.states[ACPI_STATE_D0].resources;
+
+ for (i = 0; i < list->count; i++) {
+ ret = __acpi_power_resource_register_device(powered_device,
+ list->handles[i]);
+
+ if (ret) {
+ acpi_power_resource_unregister_device(dev, handle);
+ break;
+ }
+ }
+
+ return ret;
+
+no_power_resource:
+ printk(KERN_WARNING PREFIX "Invalid Power Resource to register!");
+ return -ENODEV;
+}
+
/**
* acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in
* ACPI 3.0) _PSW (Power State Wake)
@@ -500,14 +662,14 @@ int acpi_power_transition(struct acpi_device *device, int state)
{
int result;
- if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3))
+ if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
return -EINVAL;
if (device->power.state == state)
return 0;
if ((device->power.state < ACPI_STATE_D0)
- || (device->power.state > ACPI_STATE_D3))
+ || (device->power.state > ACPI_STATE_D3_COLD))
return -ENODEV;
/* TBD: Resources must be ordered. */
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 2801b41..0734086 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -46,7 +46,6 @@
#include <linux/slab.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/cpu.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
@@ -68,6 +67,7 @@
#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
#define ACPI_PROCESSOR_NOTIFY_POWER 0x81
#define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82
+#define ACPI_PROCESSOR_DEVICE_HID "ACPI0007"
#define ACPI_PROCESSOR_LIMIT_USER 0
#define ACPI_PROCESSOR_LIMIT_THERMAL 1
@@ -88,7 +88,7 @@ static int acpi_processor_start(struct acpi_processor *pr);
static const struct acpi_device_id processor_device_ids[] = {
{ACPI_PROCESSOR_OBJECT_HID, 0},
- {"ACPI0007", 0},
+ {ACPI_PROCESSOR_DEVICE_HID, 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, processor_device_ids);
@@ -536,8 +536,8 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
return -ENOMEM;
if (!zalloc_cpumask_var(&pr->throttling.shared_cpu_map, GFP_KERNEL)) {
- kfree(pr);
- return -ENOMEM;
+ result = -ENOMEM;
+ goto err_free_pr;
}
pr->handle = device->handle;
@@ -577,7 +577,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
dev = get_cpu_device(pr->id);
if (sysfs_create_link(&device->dev.kobj, &dev->kobj, "sysdev")) {
result = -EFAULT;
- goto err_free_cpumask;
+ goto err_clear_processor;
}
/*
@@ -595,9 +595,15 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
err_remove_sysfs:
sysfs_remove_link(&device->dev.kobj, "sysdev");
+err_clear_processor:
+ /*
+ * processor_device_array is not cleared to allow checks for buggy BIOS
+ */
+ per_cpu(processors, pr->id) = NULL;
err_free_cpumask:
free_cpumask_var(pr->throttling.shared_cpu_map);
-
+err_free_pr:
+ kfree(pr);
return result;
}
@@ -742,20 +748,46 @@ static void acpi_processor_hotplug_notify(acpi_handle handle,
return;
}
+static acpi_status is_processor_device(acpi_handle handle)
+{
+ struct acpi_device_info *info;
+ char *hid;
+ acpi_status status;
+
+ status = acpi_get_object_info(handle, &info);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ if (info->type == ACPI_TYPE_PROCESSOR) {
+ kfree(info);
+ return AE_OK; /* found a processor object */
+ }
+
+ if (!(info->valid & ACPI_VALID_HID)) {
+ kfree(info);
+ return AE_ERROR;
+ }
+
+ hid = info->hardware_id.string;
+ if ((hid == NULL) || strcmp(hid, ACPI_PROCESSOR_DEVICE_HID)) {
+ kfree(info);
+ return AE_ERROR;
+ }
+
+ kfree(info);
+ return AE_OK; /* found a processor device object */
+}
+
static acpi_status
processor_walk_namespace_cb(acpi_handle handle,
u32 lvl, void *context, void **rv)
{
acpi_status status;
int *action = context;
- acpi_object_type type = 0;
- status = acpi_get_type(handle, &type);
+ status = is_processor_device(handle);
if (ACPI_FAILURE(status))
- return (AE_OK);
-
- if (type != ACPI_TYPE_PROCESSOR)
- return (AE_OK);
+ return AE_OK; /* not a processor; continue to walk */
switch (*action) {
case INSTALL_NOTIFY_HANDLER:
@@ -773,7 +805,8 @@ processor_walk_namespace_cb(acpi_handle handle,
break;
}
- return (AE_OK);
+ /* found a processor; skip walking underneath */
+ return AE_CTRL_DEPTH;
}
static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr)
@@ -831,7 +864,7 @@ void acpi_processor_install_hotplug_notify(void)
{
#ifdef CONFIG_ACPI_HOTPLUG_CPU
int action = INSTALL_NOTIFY_HANDLER;
- acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
+ acpi_walk_namespace(ACPI_TYPE_ANY,
ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
processor_walk_namespace_cb, NULL, &action, NULL);
@@ -844,7 +877,7 @@ void acpi_processor_uninstall_hotplug_notify(void)
{
#ifdef CONFIG_ACPI_HOTPLUG_CPU
int action = UNINSTALL_NOTIFY_HANDLER;
- acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
+ acpi_walk_namespace(ACPI_TYPE_ANY,
ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
processor_walk_namespace_cb, NULL, &action, NULL);
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 0e8e2de..b3447f6 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -770,6 +770,35 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
return index;
}
+
+/**
+ * acpi_idle_play_dead - enters an ACPI state for long-term idle (i.e. off-lining)
+ * @dev: the target CPU
+ * @index: the index of suggested state
+ */
+static int acpi_idle_play_dead(struct cpuidle_device *dev, int index)
+{
+ struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
+ struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
+
+ ACPI_FLUSH_CPU_CACHE();
+
+ while (1) {
+
+ if (cx->entry_method == ACPI_CSTATE_HALT)
+ halt();
+ else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) {
+ inb(cx->address);
+ /* See comment in acpi_idle_do_entry() */
+ inl(acpi_gbl_FADT.xpm_timer_block.address);
+ } else
+ return -ENODEV;
+ }
+
+ /* Never reached */
+ return 0;
+}
+
/**
* acpi_idle_enter_simple - enters an ACPI state without BM handling
* @dev: the target CPU
@@ -1077,12 +1106,14 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
state->flags |= CPUIDLE_FLAG_TIME_VALID;
state->enter = acpi_idle_enter_c1;
+ state->enter_dead = acpi_idle_play_dead;
drv->safe_state_index = count;
break;
case ACPI_STATE_C2:
state->flags |= CPUIDLE_FLAG_TIME_VALID;
state->enter = acpi_idle_enter_simple;
+ state->enter_dead = acpi_idle_play_dead;
drv->safe_state_index = count;
break;
@@ -1159,8 +1190,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr)
* to make the code that updates C-States be called once.
*/
- if (smp_processor_id() == 0 &&
- cpuidle_get_driver() == &acpi_idle_driver) {
+ if (pr->id == 0 && cpuidle_get_driver() == &acpi_idle_driver) {
cpuidle_pause_and_lock();
/* Protect against cpu-hotplug */
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
index 3b599ab..641b545 100644
--- a/drivers/acpi/processor_thermal.c
+++ b/drivers/acpi/processor_thermal.c
@@ -57,6 +57,27 @@ ACPI_MODULE_NAME("processor_thermal");
static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_pctg);
static unsigned int acpi_thermal_cpufreq_is_init = 0;
+#define reduction_pctg(cpu) \
+ per_cpu(cpufreq_thermal_reduction_pctg, phys_package_first_cpu(cpu))
+
+/*
+ * Emulate "per package data" using per cpu data (which should really be
+ * provided elsewhere)
+ *
+ * Note we can lose a CPU on cpu hotunplug, in this case we forget the state
+ * temporarily. Fortunately that's not a big issue here (I hope)
+ */
+static int phys_package_first_cpu(int cpu)
+{
+ int i;
+ int id = topology_physical_package_id(cpu);
+
+ for_each_online_cpu(i)
+ if (topology_physical_package_id(i) == id)
+ return i;
+ return 0;
+}
+
static int cpu_has_cpufreq(unsigned int cpu)
{
struct cpufreq_policy policy;
@@ -76,7 +97,7 @@ static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb,
max_freq = (
policy->cpuinfo.max_freq *
- (100 - per_cpu(cpufreq_thermal_reduction_pctg, policy->cpu) * 20)
+ (100 - reduction_pctg(policy->cpu) * 20)
) / 100;
cpufreq_verify_within_limits(policy, 0, max_freq);
@@ -102,16 +123,28 @@ static int cpufreq_get_cur_state(unsigned int cpu)
if (!cpu_has_cpufreq(cpu))
return 0;
- return per_cpu(cpufreq_thermal_reduction_pctg, cpu);
+ return reduction_pctg(cpu);
}
static int cpufreq_set_cur_state(unsigned int cpu, int state)
{
+ int i;
+
if (!cpu_has_cpufreq(cpu))
return 0;
- per_cpu(cpufreq_thermal_reduction_pctg, cpu) = state;
- cpufreq_update_policy(cpu);
+ reduction_pctg(cpu) = state;
+
+ /*
+ * Update all the CPUs in the same package because they all
+ * contribute to the temperature and often share the same
+ * frequency.
+ */
+ for_each_online_cpu(i) {
+ if (topology_physical_package_id(i) ==
+ topology_physical_package_id(cpu))
+ cpufreq_update_policy(i);
+ }
return 0;
}
@@ -119,10 +152,6 @@ void acpi_thermal_cpufreq_init(void)
{
int i;
- for (i = 0; i < nr_cpu_ids; i++)
- if (cpu_present(i))
- per_cpu(cpufreq_thermal_reduction_pctg, i) = 0;
-
i = cpufreq_register_notifier(&acpi_thermal_cpufreq_notifier_block,
CPUFREQ_POLICY_NOTIFIER);
if (!i)
diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c
index 605a295..1d02b7b 100644
--- a/drivers/acpi/processor_throttling.c
+++ b/drivers/acpi/processor_throttling.c
@@ -769,7 +769,7 @@ static int acpi_read_throttling_status(struct acpi_processor *pr,
u64 *value)
{
u32 bit_width, bit_offset;
- u64 ptc_value;
+ u32 ptc_value;
u64 ptc_mask;
struct acpi_processor_throttling *throttling;
int ret = -1;
@@ -777,12 +777,11 @@ static int acpi_read_throttling_status(struct acpi_processor *pr,
throttling = &pr->throttling;
switch (throttling->status_register.space_id) {
case ACPI_ADR_SPACE_SYSTEM_IO:
- ptc_value = 0;
bit_width = throttling->status_register.bit_width;
bit_offset = throttling->status_register.bit_offset;
acpi_os_read_port((acpi_io_address) throttling->status_register.
- address, (u32 *) &ptc_value,
+ address, &ptc_value,
(u32) (bit_width + bit_offset));
ptc_mask = (1 << bit_width) - 1;
*value = (u64) ((ptc_value >> bit_offset) & ptc_mask);
diff --git a/drivers/acpi/reboot.c b/drivers/acpi/reboot.c
index a6c77e8b..c1d6124 100644
--- a/drivers/acpi/reboot.c
+++ b/drivers/acpi/reboot.c
@@ -23,8 +23,7 @@ void acpi_reboot(void)
/* Is the reset register supported? The spec says we should be
* checking the bit width and bit offset, but Windows ignores
* these fields */
- if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER))
- return;
+ /* Ignore also acpi_gbl_FADT.flags.ACPI_FADT_RESET_REGISTER */
reset_value = acpi_gbl_FADT.reset_value;
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 8ab80ba..767e2dc 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -880,18 +880,22 @@ static int acpi_bus_get_power_flags(struct acpi_device *device)
int j;
device->power.flags.power_resources = 1;
- ps->flags.valid = 1;
for (j = 0; j < ps->resources.count; j++)
acpi_bus_add_power_resource(ps->resources.handles[j]);
}
+ /* The exist of _PR3 indicates D3Cold support */
+ if (i == ACPI_STATE_D3) {
+ status = acpi_get_handle(device->handle, object_name, &handle);
+ if (ACPI_SUCCESS(status))
+ device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1;
+ }
+
/* Evaluate "_PSx" to see if we can do explicit sets */
object_name[2] = 'S';
status = acpi_get_handle(device->handle, object_name, &handle);
- if (ACPI_SUCCESS(status)) {
+ if (ACPI_SUCCESS(status))
ps->flags.explicit_set = 1;
- ps->flags.valid = 1;
- }
/* State is valid if we have some power control */
if (ps->resources.count || ps->flags.explicit_set)
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index ca191ff..1d661b5 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -17,6 +17,8 @@
#include <linux/suspend.h>
#include <linux/reboot.h>
#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <asm/io.h>
@@ -26,6 +28,24 @@
#include "internal.h"
#include "sleep.h"
+static unsigned int gts, bfs;
+module_param(gts, uint, 0644);
+module_param(bfs, uint, 0644);
+MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend.");
+MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".);
+
+static u8 wake_sleep_flags(void)
+{
+ u8 flags = ACPI_NO_OPTIONAL_METHODS;
+
+ if (gts)
+ flags |= ACPI_EXECUTE_GTS;
+ if (bfs)
+ flags |= ACPI_EXECUTE_BFS;
+
+ return flags;
+}
+
static u8 sleep_states[ACPI_S_STATE_COUNT];
static void acpi_sleep_tts_switch(u32 acpi_state)
@@ -243,6 +263,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
{
acpi_status status = AE_OK;
u32 acpi_state = acpi_target_sleep_state;
+ u8 flags = wake_sleep_flags();
int error;
ACPI_FLUSH_CPU_CACHE();
@@ -250,7 +271,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
switch (acpi_state) {
case ACPI_STATE_S1:
barrier();
- status = acpi_enter_sleep_state(acpi_state);
+ status = acpi_enter_sleep_state(acpi_state, flags);
break;
case ACPI_STATE_S3:
@@ -265,7 +286,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
/* Reprogram control registers and execute _BFS */
- acpi_leave_sleep_state_prep(acpi_state);
+ acpi_leave_sleep_state_prep(acpi_state, flags);
/* ACPI 3.0 specs (P62) says that it's the responsibility
* of the OSPM to clear the status bit [ implying that the
@@ -529,27 +550,30 @@ static int acpi_hibernation_begin(void)
static int acpi_hibernation_enter(void)
{
+ u8 flags = wake_sleep_flags();
acpi_status status = AE_OK;
ACPI_FLUSH_CPU_CACHE();
/* This shouldn't return. If it returns, we have a problem */
- status = acpi_enter_sleep_state(ACPI_STATE_S4);
+ status = acpi_enter_sleep_state(ACPI_STATE_S4, flags);
/* Reprogram control registers and execute _BFS */
- acpi_leave_sleep_state_prep(ACPI_STATE_S4);
+ acpi_leave_sleep_state_prep(ACPI_STATE_S4, flags);
return ACPI_SUCCESS(status) ? 0 : -EFAULT;
}
static void acpi_hibernation_leave(void)
{
+ u8 flags = wake_sleep_flags();
+
/*
* If ACPI is not enabled by the BIOS and the boot kernel, we need to
* enable it here.
*/
acpi_enable();
/* Reprogram control registers and execute _BFS */
- acpi_leave_sleep_state_prep(ACPI_STATE_S4);
+ acpi_leave_sleep_state_prep(ACPI_STATE_S4, flags);
/* Check the hardware signature */
if (facs && s4_hardware_signature != facs->hardware_signature) {
printk(KERN_EMERG "ACPI: Hardware changed while hibernated, "
@@ -730,6 +754,40 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p)
#ifdef CONFIG_PM_SLEEP
/**
+ * acpi_pm_device_run_wake - Enable/disable wake-up for given device.
+ * @phys_dev: Device to enable/disable the platform to wake-up the system for.
+ * @enable: Whether enable or disable the wake-up functionality.
+ *
+ * Find the ACPI device object corresponding to @pci_dev and try to
+ * enable/disable the GPE associated with it.
+ */
+int acpi_pm_device_run_wake(struct device *phys_dev, bool enable)
+{
+ struct acpi_device *dev;
+ acpi_handle handle;
+
+ if (!device_run_wake(phys_dev))
+ return -EINVAL;
+
+ handle = DEVICE_ACPI_HANDLE(phys_dev);
+ if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &dev))) {
+ dev_dbg(phys_dev, "ACPI handle has no context in %s!\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ if (enable) {
+ acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0);
+ acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);
+ } else {
+ acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);
+ acpi_disable_wakeup_device_power(dev);
+ }
+
+ return 0;
+}
+
+/**
* acpi_pm_device_sleep_wake - enable or disable the system wake-up
* capability of given device
* @dev: device to handle
@@ -770,10 +828,12 @@ static void acpi_power_off_prepare(void)
static void acpi_power_off(void)
{
+ u8 flags = wake_sleep_flags();
+
/* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */
printk(KERN_DEBUG "%s called\n", __func__);
local_irq_disable();
- acpi_enter_sleep_state(ACPI_STATE_S5);
+ acpi_enter_sleep_state(ACPI_STATE_S5, flags);
}
/*
@@ -788,13 +848,13 @@ static void __init acpi_gts_bfs_check(void)
{
acpi_handle dummy;
- if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_NAME__GTS, &dummy)))
+ if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_PATHNAME__GTS, &dummy)))
{
printk(KERN_NOTICE PREFIX "BIOS offers _GTS\n");
printk(KERN_NOTICE PREFIX "If \"acpi.gts=1\" improves suspend, "
"please notify linux-acpi@vger.kernel.org\n");
}
- if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_NAME__BFS, &dummy)))
+ if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_PATHNAME__BFS, &dummy)))
{
printk(KERN_NOTICE PREFIX "BIOS offers _BFS\n");
printk(KERN_NOTICE PREFIX "If \"acpi.bfs=1\" improves resume, "
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 48fbc64..7dbebea 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -941,13 +941,13 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz)
if (!tz)
return -EINVAL;
- /* Get temperature [_TMP] (required) */
- result = acpi_thermal_get_temperature(tz);
+ /* Get trip points [_CRT, _PSV, etc.] (required) */
+ result = acpi_thermal_get_trip_points(tz);
if (result)
return result;
- /* Get trip points [_CRT, _PSV, etc.] (required) */
- result = acpi_thermal_get_trip_points(tz);
+ /* Get temperature [_TMP] (required) */
+ result = acpi_thermal_get_temperature(tz);
if (result)
return result;
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index eaef02a..9577b6f 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -548,27 +548,27 @@ acpi_video_device_EDID(struct acpi_video_device *device,
* 1. The system BIOS should NOT automatically control the brightness
* level of the LCD when the power changes from AC to DC.
* Return Value:
- * -1 wrong arg.
+ * -EINVAL wrong arg.
*/
static int
acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
{
- u64 status = 0;
+ acpi_status status;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
- if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) {
- status = -1;
- goto Failed;
- }
+ if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1)
+ return -EINVAL;
arg0.integer.value = (lcd_flag << 2) | bios_flag;
video->dos_setting = arg0.integer.value;
- acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL);
+ status = acpi_evaluate_object(video->device->handle, "_DOS",
+ &args, NULL);
+ if (ACPI_FAILURE(status))
+ return -EIO;
- Failed:
- return status;
+ return 0;
}
/*
@@ -1343,15 +1343,17 @@ static int
acpi_video_bus_get_devices(struct acpi_video_bus *video,
struct acpi_device *device)
{
- int status = 0;
+ int status;
struct acpi_device *dev;
- acpi_video_device_enumerate(video);
+ status = acpi_video_device_enumerate(video);
+ if (status)
+ return status;
list_for_each_entry(dev, &device->children, node) {
status = acpi_video_bus_get_one_device(dev, video);
- if (ACPI_FAILURE(status)) {
+ if (status) {
printk(KERN_WARNING PREFIX
"Can't attach device\n");
continue;
@@ -1653,15 +1655,20 @@ static int acpi_video_bus_add(struct acpi_device *device)
mutex_init(&video->device_list_lock);
INIT_LIST_HEAD(&video->video_device_list);
- acpi_video_bus_get_devices(video, device);
- acpi_video_bus_start_devices(video);
+ error = acpi_video_bus_get_devices(video, device);
+ if (error)
+ goto err_free_video;
video->input = input = input_allocate_device();
if (!input) {
error = -ENOMEM;
- goto err_stop_video;
+ goto err_put_video;
}
+ error = acpi_video_bus_start_devices(video);
+ if (error)
+ goto err_free_input_dev;
+
snprintf(video->phys, sizeof(video->phys),
"%s/video/input0", acpi_device_hid(video->device));
@@ -1682,7 +1689,7 @@ static int acpi_video_bus_add(struct acpi_device *device)
error = input_register_device(input);
if (error)
- goto err_free_input_dev;
+ goto err_stop_video;
printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n",
ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
@@ -1692,14 +1699,19 @@ static int acpi_video_bus_add(struct acpi_device *device)
video->pm_nb.notifier_call = acpi_video_resume;
video->pm_nb.priority = 0;
- register_pm_notifier(&video->pm_nb);
+ error = register_pm_notifier(&video->pm_nb);
+ if (error)
+ goto err_unregister_input_dev;
return 0;
- err_free_input_dev:
- input_free_device(input);
+ err_unregister_input_dev:
+ input_unregister_device(input);
err_stop_video:
acpi_video_bus_stop_devices(video);
+ err_free_input_dev:
+ input_free_device(input);
+ err_put_video:
acpi_video_bus_put_devices(video);
kfree(video->attached_array);
err_free_video:
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index f3f0fe7..45d8097 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -23,7 +23,7 @@
* Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B)
* are available, video.ko should be used to handle the device.
*
- * Otherwise vendor specific drivers like thinkpad_acpi, asus_acpi,
+ * Otherwise vendor specific drivers like thinkpad_acpi, asus-laptop,
* sony_acpi,... can take care about backlight brightness.
*
* If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m)
diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c
index 6ff612d..2059ee4 100644
--- a/drivers/atm/eni.c
+++ b/drivers/atm/eni.c
@@ -19,7 +19,6 @@
#include <linux/atm_eni.h>
#include <linux/bitops.h>
#include <linux/slab.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <linux/atomic.h>
#include <asm/uaccess.h>
diff --git a/drivers/atm/firestream.c b/drivers/atm/firestream.c
index 5072f8a..86fed1b 100644
--- a/drivers/atm/firestream.c
+++ b/drivers/atm/firestream.c
@@ -49,7 +49,6 @@
#include <linux/bitops.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
-#include <asm/system.h>
#include <asm/string.h>
#include <asm/io.h>
#include <linux/atomic.h>
diff --git a/drivers/atm/horizon.c b/drivers/atm/horizon.c
index b812103..75fd691 100644
--- a/drivers/atm/horizon.c
+++ b/drivers/atm/horizon.c
@@ -43,7 +43,6 @@
#include <linux/wait.h>
#include <linux/slab.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <linux/atomic.h>
#include <asm/uaccess.h>
diff --git a/drivers/atm/idt77105.c b/drivers/atm/idt77105.c
index 487a547..45d5063 100644
--- a/drivers/atm/idt77105.c
+++ b/drivers/atm/idt77105.c
@@ -16,7 +16,6 @@
#include <linux/atm_idt77105.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
-#include <asm/system.h>
#include <asm/param.h>
#include <asm/uaccess.h>
diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c
index 9e373ba..d438601 100644
--- a/drivers/atm/iphase.c
+++ b/drivers/atm/iphase.c
@@ -56,7 +56,6 @@
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/slab.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <linux/atomic.h>
#include <asm/uaccess.h>
diff --git a/drivers/atm/suni.c b/drivers/atm/suni.c
index 90f1ccc..0215934 100644
--- a/drivers/atm/suni.c
+++ b/drivers/atm/suni.c
@@ -22,7 +22,6 @@
#include <linux/capability.h>
#include <linux/atm_suni.h>
#include <linux/slab.h>
-#include <asm/system.h>
#include <asm/param.h>
#include <asm/uaccess.h>
#include <linux/atomic.h>
diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c
index d889f56..abe4e20 100644
--- a/drivers/atm/zatm.c
+++ b/drivers/atm/zatm.c
@@ -24,7 +24,6 @@
#include <linux/wait.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
-#include <asm/system.h>
#include <asm/string.h>
#include <asm/io.h>
#include <linux/atomic.h>
diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c
index e38ad24..07cbbc6 100644
--- a/drivers/base/dma-buf.c
+++ b/drivers/base/dma-buf.c
@@ -71,7 +71,7 @@ static inline int is_dma_buf_file(struct file *file)
* ops, or error in allocating struct dma_buf, will return negative error.
*
*/
-struct dma_buf *dma_buf_export(void *priv, struct dma_buf_ops *ops,
+struct dma_buf *dma_buf_export(void *priv, const struct dma_buf_ops *ops,
size_t size, int flags)
{
struct dma_buf *dmabuf;
@@ -80,7 +80,9 @@ struct dma_buf *dma_buf_export(void *priv, struct dma_buf_ops *ops,
if (WARN_ON(!priv || !ops
|| !ops->map_dma_buf
|| !ops->unmap_dma_buf
- || !ops->release)) {
+ || !ops->release
+ || !ops->kmap_atomic
+ || !ops->kmap)) {
return ERR_PTR(-EINVAL);
}
@@ -107,17 +109,18 @@ EXPORT_SYMBOL_GPL(dma_buf_export);
/**
* dma_buf_fd - returns a file descriptor for the given dma_buf
* @dmabuf: [in] pointer to dma_buf for which fd is required.
+ * @flags: [in] flags to give to fd
*
* On success, returns an associated 'fd'. Else, returns error.
*/
-int dma_buf_fd(struct dma_buf *dmabuf)
+int dma_buf_fd(struct dma_buf *dmabuf, int flags)
{
int error, fd;
if (!dmabuf || !dmabuf->file)
return -EINVAL;
- error = get_unused_fd();
+ error = get_unused_fd_flags(flags);
if (error < 0)
return error;
fd = error;
@@ -185,17 +188,18 @@ struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
struct dma_buf_attachment *attach;
int ret;
- if (WARN_ON(!dmabuf || !dev || !dmabuf->ops))
+ if (WARN_ON(!dmabuf || !dev))
return ERR_PTR(-EINVAL);
attach = kzalloc(sizeof(struct dma_buf_attachment), GFP_KERNEL);
if (attach == NULL)
- goto err_alloc;
-
- mutex_lock(&dmabuf->lock);
+ return ERR_PTR(-ENOMEM);
attach->dev = dev;
attach->dmabuf = dmabuf;
+
+ mutex_lock(&dmabuf->lock);
+
if (dmabuf->ops->attach) {
ret = dmabuf->ops->attach(dmabuf, dev, attach);
if (ret)
@@ -206,8 +210,6 @@ struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
mutex_unlock(&dmabuf->lock);
return attach;
-err_alloc:
- return ERR_PTR(-ENOMEM);
err_attach:
kfree(attach);
mutex_unlock(&dmabuf->lock);
@@ -224,7 +226,7 @@ EXPORT_SYMBOL_GPL(dma_buf_attach);
*/
void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
{
- if (WARN_ON(!dmabuf || !attach || !dmabuf->ops))
+ if (WARN_ON(!dmabuf || !attach))
return;
mutex_lock(&dmabuf->lock);
@@ -255,13 +257,10 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
might_sleep();
- if (WARN_ON(!attach || !attach->dmabuf || !attach->dmabuf->ops))
+ if (WARN_ON(!attach || !attach->dmabuf))
return ERR_PTR(-EINVAL);
- mutex_lock(&attach->dmabuf->lock);
- if (attach->dmabuf->ops->map_dma_buf)
- sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction);
- mutex_unlock(&attach->dmabuf->lock);
+ sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction);
return sg_table;
}
@@ -273,19 +272,137 @@ EXPORT_SYMBOL_GPL(dma_buf_map_attachment);
* dma_buf_ops.
* @attach: [in] attachment to unmap buffer from
* @sg_table: [in] scatterlist info of the buffer to unmap
+ * @direction: [in] direction of DMA transfer
*
*/
void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
- struct sg_table *sg_table)
+ struct sg_table *sg_table,
+ enum dma_data_direction direction)
{
- if (WARN_ON(!attach || !attach->dmabuf || !sg_table
- || !attach->dmabuf->ops))
+ if (WARN_ON(!attach || !attach->dmabuf || !sg_table))
return;
- mutex_lock(&attach->dmabuf->lock);
- if (attach->dmabuf->ops->unmap_dma_buf)
- attach->dmabuf->ops->unmap_dma_buf(attach, sg_table);
- mutex_unlock(&attach->dmabuf->lock);
-
+ attach->dmabuf->ops->unmap_dma_buf(attach, sg_table,
+ direction);
}
EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);
+
+
+/**
+ * dma_buf_begin_cpu_access - Must be called before accessing a dma_buf from the
+ * cpu in the kernel context. Calls begin_cpu_access to allow exporter-specific
+ * preparations. Coherency is only guaranteed in the specified range for the
+ * specified access direction.
+ * @dma_buf: [in] buffer to prepare cpu access for.
+ * @start: [in] start of range for cpu access.
+ * @len: [in] length of range for cpu access.
+ * @direction: [in] length of range for cpu access.
+ *
+ * Can return negative error values, returns 0 on success.
+ */
+int dma_buf_begin_cpu_access(struct dma_buf *dmabuf, size_t start, size_t len,
+ enum dma_data_direction direction)
+{
+ int ret = 0;
+
+ if (WARN_ON(!dmabuf))
+ return -EINVAL;
+
+ if (dmabuf->ops->begin_cpu_access)
+ ret = dmabuf->ops->begin_cpu_access(dmabuf, start, len, direction);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access);
+
+/**
+ * dma_buf_end_cpu_access - Must be called after accessing a dma_buf from the
+ * cpu in the kernel context. Calls end_cpu_access to allow exporter-specific
+ * actions. Coherency is only guaranteed in the specified range for the
+ * specified access direction.
+ * @dma_buf: [in] buffer to complete cpu access for.
+ * @start: [in] start of range for cpu access.
+ * @len: [in] length of range for cpu access.
+ * @direction: [in] length of range for cpu access.
+ *
+ * This call must always succeed.
+ */
+void dma_buf_end_cpu_access(struct dma_buf *dmabuf, size_t start, size_t len,
+ enum dma_data_direction direction)
+{
+ WARN_ON(!dmabuf);
+
+ if (dmabuf->ops->end_cpu_access)
+ dmabuf->ops->end_cpu_access(dmabuf, start, len, direction);
+}
+EXPORT_SYMBOL_GPL(dma_buf_end_cpu_access);
+
+/**
+ * dma_buf_kmap_atomic - Map a page of the buffer object into kernel address
+ * space. The same restrictions as for kmap_atomic and friends apply.
+ * @dma_buf: [in] buffer to map page from.
+ * @page_num: [in] page in PAGE_SIZE units to map.
+ *
+ * This call must always succeed, any necessary preparations that might fail
+ * need to be done in begin_cpu_access.
+ */
+void *dma_buf_kmap_atomic(struct dma_buf *dmabuf, unsigned long page_num)
+{
+ WARN_ON(!dmabuf);
+
+ return dmabuf->ops->kmap_atomic(dmabuf, page_num);
+}
+EXPORT_SYMBOL_GPL(dma_buf_kmap_atomic);
+
+/**
+ * dma_buf_kunmap_atomic - Unmap a page obtained by dma_buf_kmap_atomic.
+ * @dma_buf: [in] buffer to unmap page from.
+ * @page_num: [in] page in PAGE_SIZE units to unmap.
+ * @vaddr: [in] kernel space pointer obtained from dma_buf_kmap_atomic.
+ *
+ * This call must always succeed.
+ */
+void dma_buf_kunmap_atomic(struct dma_buf *dmabuf, unsigned long page_num,
+ void *vaddr)
+{
+ WARN_ON(!dmabuf);
+
+ if (dmabuf->ops->kunmap_atomic)
+ dmabuf->ops->kunmap_atomic(dmabuf, page_num, vaddr);
+}
+EXPORT_SYMBOL_GPL(dma_buf_kunmap_atomic);
+
+/**
+ * dma_buf_kmap - Map a page of the buffer object into kernel address space. The
+ * same restrictions as for kmap and friends apply.
+ * @dma_buf: [in] buffer to map page from.
+ * @page_num: [in] page in PAGE_SIZE units to map.
+ *
+ * This call must always succeed, any necessary preparations that might fail
+ * need to be done in begin_cpu_access.
+ */
+void *dma_buf_kmap(struct dma_buf *dmabuf, unsigned long page_num)
+{
+ WARN_ON(!dmabuf);
+
+ return dmabuf->ops->kmap(dmabuf, page_num);
+}
+EXPORT_SYMBOL_GPL(dma_buf_kmap);
+
+/**
+ * dma_buf_kunmap - Unmap a page obtained by dma_buf_kmap.
+ * @dma_buf: [in] buffer to unmap page from.
+ * @page_num: [in] page in PAGE_SIZE units to unmap.
+ * @vaddr: [in] kernel space pointer obtained from dma_buf_kmap.
+ *
+ * This call must always succeed.
+ */
+void dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long page_num,
+ void *vaddr)
+{
+ WARN_ON(!dmabuf);
+
+ if (dmabuf->ops->kunmap)
+ dmabuf->ops->kunmap(dmabuf, page_num, vaddr);
+}
+EXPORT_SYMBOL_GPL(dma_buf_kunmap);
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 744f078..76a0823 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -202,7 +202,6 @@ static int slow_floppy;
#include <asm/dma.h>
#include <asm/irq.h>
-#include <asm/system.h>
static int FLOPPY_IRQ = 6;
static int FLOPPY_DMA = 2;
diff --git a/drivers/block/hd.c b/drivers/block/hd.c
index b52c9ca..bf397bf 100644
--- a/drivers/block/hd.c
+++ b/drivers/block/hd.c
@@ -44,7 +44,6 @@
#define HD_IRQ 14
#define REALLY_SLOW_IO
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index c3f0ee1..061427a 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -34,12 +34,11 @@
#include <linux/kthread.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#include <asm/types.h>
#include <linux/nbd.h>
-#define LO_MAGIC 0x68797548
+#define NBD_MAGIC 0x68797548
#ifdef NDEBUG
#define dprintk(flags, fmt...)
@@ -116,7 +115,7 @@ static void nbd_end_request(struct request *req)
spin_unlock_irqrestore(q->queue_lock, flags);
}
-static void sock_shutdown(struct nbd_device *lo, int lock)
+static void sock_shutdown(struct nbd_device *nbd, int lock)
{
/* Forcibly shutdown the socket causing all listeners
* to error
@@ -125,14 +124,14 @@ static void sock_shutdown(struct nbd_device *lo, int lock)
* there should be a more generic interface rather than
* calling socket ops directly here */
if (lock)
- mutex_lock(&lo->tx_lock);
- if (lo->sock) {
- dev_warn(disk_to_dev(lo->disk), "shutting down socket\n");
- kernel_sock_shutdown(lo->sock, SHUT_RDWR);
- lo->sock = NULL;
+ mutex_lock(&nbd->tx_lock);
+ if (nbd->sock) {
+ dev_warn(disk_to_dev(nbd->disk), "shutting down socket\n");
+ kernel_sock_shutdown(nbd->sock, SHUT_RDWR);
+ nbd->sock = NULL;
}
if (lock)
- mutex_unlock(&lo->tx_lock);
+ mutex_unlock(&nbd->tx_lock);
}
static void nbd_xmit_timeout(unsigned long arg)
@@ -147,17 +146,17 @@ static void nbd_xmit_timeout(unsigned long arg)
/*
* Send or receive packet.
*/
-static int sock_xmit(struct nbd_device *lo, int send, void *buf, int size,
+static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size,
int msg_flags)
{
- struct socket *sock = lo->sock;
+ struct socket *sock = nbd->sock;
int result;
struct msghdr msg;
struct kvec iov;
sigset_t blocked, oldset;
if (unlikely(!sock)) {
- dev_err(disk_to_dev(lo->disk),
+ dev_err(disk_to_dev(nbd->disk),
"Attempted %s on closed socket in sock_xmit\n",
(send ? "send" : "recv"));
return -EINVAL;
@@ -181,15 +180,15 @@ static int sock_xmit(struct nbd_device *lo, int send, void *buf, int size,
if (send) {
struct timer_list ti;
- if (lo->xmit_timeout) {
+ if (nbd->xmit_timeout) {
init_timer(&ti);
ti.function = nbd_xmit_timeout;
ti.data = (unsigned long)current;
- ti.expires = jiffies + lo->xmit_timeout;
+ ti.expires = jiffies + nbd->xmit_timeout;
add_timer(&ti);
}
result = kernel_sendmsg(sock, &msg, &iov, 1, size);
- if (lo->xmit_timeout)
+ if (nbd->xmit_timeout)
del_timer_sync(&ti);
} else
result = kernel_recvmsg(sock, &msg, &iov, 1, size,
@@ -201,7 +200,7 @@ static int sock_xmit(struct nbd_device *lo, int send, void *buf, int size,
task_pid_nr(current), current->comm,
dequeue_signal_lock(current, &current->blocked, &info));
result = -EINTR;
- sock_shutdown(lo, !send);
+ sock_shutdown(nbd, !send);
break;
}
@@ -219,18 +218,19 @@ static int sock_xmit(struct nbd_device *lo, int send, void *buf, int size,
return result;
}
-static inline int sock_send_bvec(struct nbd_device *lo, struct bio_vec *bvec,
+static inline int sock_send_bvec(struct nbd_device *nbd, struct bio_vec *bvec,
int flags)
{
int result;
void *kaddr = kmap(bvec->bv_page);
- result = sock_xmit(lo, 1, kaddr + bvec->bv_offset, bvec->bv_len, flags);
+ result = sock_xmit(nbd, 1, kaddr + bvec->bv_offset,
+ bvec->bv_len, flags);
kunmap(bvec->bv_page);
return result;
}
/* always call with the tx_lock held */
-static int nbd_send_req(struct nbd_device *lo, struct request *req)
+static int nbd_send_req(struct nbd_device *nbd, struct request *req)
{
int result, flags;
struct nbd_request request;
@@ -243,14 +243,14 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req)
memcpy(request.handle, &req, sizeof(req));
dprintk(DBG_TX, "%s: request %p: sending control (%s@%llu,%uB)\n",
- lo->disk->disk_name, req,
+ nbd->disk->disk_name, req,
nbdcmd_to_ascii(nbd_cmd(req)),
(unsigned long long)blk_rq_pos(req) << 9,
blk_rq_bytes(req));
- result = sock_xmit(lo, 1, &request, sizeof(request),
+ result = sock_xmit(nbd, 1, &request, sizeof(request),
(nbd_cmd(req) == NBD_CMD_WRITE) ? MSG_MORE : 0);
if (result <= 0) {
- dev_err(disk_to_dev(lo->disk),
+ dev_err(disk_to_dev(nbd->disk),
"Send control failed (result %d)\n", result);
goto error_out;
}
@@ -267,10 +267,10 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req)
if (!rq_iter_last(req, iter))
flags = MSG_MORE;
dprintk(DBG_TX, "%s: request %p: sending %d bytes data\n",
- lo->disk->disk_name, req, bvec->bv_len);
- result = sock_send_bvec(lo, bvec, flags);
+ nbd->disk->disk_name, req, bvec->bv_len);
+ result = sock_send_bvec(nbd, bvec, flags);
if (result <= 0) {
- dev_err(disk_to_dev(lo->disk),
+ dev_err(disk_to_dev(nbd->disk),
"Send data failed (result %d)\n",
result);
goto error_out;
@@ -283,25 +283,25 @@ error_out:
return -EIO;
}
-static struct request *nbd_find_request(struct nbd_device *lo,
+static struct request *nbd_find_request(struct nbd_device *nbd,
struct request *xreq)
{
struct request *req, *tmp;
int err;
- err = wait_event_interruptible(lo->active_wq, lo->active_req != xreq);
+ err = wait_event_interruptible(nbd->active_wq, nbd->active_req != xreq);
if (unlikely(err))
goto out;
- spin_lock(&lo->queue_lock);
- list_for_each_entry_safe(req, tmp, &lo->queue_head, queuelist) {
+ spin_lock(&nbd->queue_lock);
+ list_for_each_entry_safe(req, tmp, &nbd->queue_head, queuelist) {
if (req != xreq)
continue;
list_del_init(&req->queuelist);
- spin_unlock(&lo->queue_lock);
+ spin_unlock(&nbd->queue_lock);
return req;
}
- spin_unlock(&lo->queue_lock);
+ spin_unlock(&nbd->queue_lock);
err = -ENOENT;
@@ -309,78 +309,78 @@ out:
return ERR_PTR(err);
}
-static inline int sock_recv_bvec(struct nbd_device *lo, struct bio_vec *bvec)
+static inline int sock_recv_bvec(struct nbd_device *nbd, struct bio_vec *bvec)
{
int result;
void *kaddr = kmap(bvec->bv_page);
- result = sock_xmit(lo, 0, kaddr + bvec->bv_offset, bvec->bv_len,
+ result = sock_xmit(nbd, 0, kaddr + bvec->bv_offset, bvec->bv_len,
MSG_WAITALL);
kunmap(bvec->bv_page);
return result;
}
/* NULL returned = something went wrong, inform userspace */
-static struct request *nbd_read_stat(struct nbd_device *lo)
+static struct request *nbd_read_stat(struct nbd_device *nbd)
{
int result;
struct nbd_reply reply;
struct request *req;
reply.magic = 0;
- result = sock_xmit(lo, 0, &reply, sizeof(reply), MSG_WAITALL);
+ result = sock_xmit(nbd, 0, &reply, sizeof(reply), MSG_WAITALL);
if (result <= 0) {
- dev_err(disk_to_dev(lo->disk),
+ dev_err(disk_to_dev(nbd->disk),
"Receive control failed (result %d)\n", result);
goto harderror;
}
if (ntohl(reply.magic) != NBD_REPLY_MAGIC) {
- dev_err(disk_to_dev(lo->disk), "Wrong magic (0x%lx)\n",
+ dev_err(disk_to_dev(nbd->disk), "Wrong magic (0x%lx)\n",
(unsigned long)ntohl(reply.magic));
result = -EPROTO;
goto harderror;
}
- req = nbd_find_request(lo, *(struct request **)reply.handle);
+ req = nbd_find_request(nbd, *(struct request **)reply.handle);
if (IS_ERR(req)) {
result = PTR_ERR(req);
if (result != -ENOENT)
goto harderror;
- dev_err(disk_to_dev(lo->disk), "Unexpected reply (%p)\n",
+ dev_err(disk_to_dev(nbd->disk), "Unexpected reply (%p)\n",
reply.handle);
result = -EBADR;
goto harderror;
}
if (ntohl(reply.error)) {
- dev_err(disk_to_dev(lo->disk), "Other side returned error (%d)\n",
+ dev_err(disk_to_dev(nbd->disk), "Other side returned error (%d)\n",
ntohl(reply.error));
req->errors++;
return req;
}
dprintk(DBG_RX, "%s: request %p: got reply\n",
- lo->disk->disk_name, req);
+ nbd->disk->disk_name, req);
if (nbd_cmd(req) == NBD_CMD_READ) {
struct req_iterator iter;
struct bio_vec *bvec;
rq_for_each_segment(bvec, req, iter) {
- result = sock_recv_bvec(lo, bvec);
+ result = sock_recv_bvec(nbd, bvec);
if (result <= 0) {
- dev_err(disk_to_dev(lo->disk), "Receive data failed (result %d)\n",
+ dev_err(disk_to_dev(nbd->disk), "Receive data failed (result %d)\n",
result);
req->errors++;
return req;
}
dprintk(DBG_RX, "%s: request %p: got %d bytes data\n",
- lo->disk->disk_name, req, bvec->bv_len);
+ nbd->disk->disk_name, req, bvec->bv_len);
}
}
return req;
harderror:
- lo->harderror = result;
+ nbd->harderror = result;
return NULL;
}
@@ -398,48 +398,48 @@ static struct device_attribute pid_attr = {
.show = pid_show,
};
-static int nbd_do_it(struct nbd_device *lo)
+static int nbd_do_it(struct nbd_device *nbd)
{
struct request *req;
int ret;
- BUG_ON(lo->magic != LO_MAGIC);
+ BUG_ON(nbd->magic != NBD_MAGIC);
- lo->pid = task_pid_nr(current);
- ret = device_create_file(disk_to_dev(lo->disk), &pid_attr);
+ nbd->pid = task_pid_nr(current);
+ ret = device_create_file(disk_to_dev(nbd->disk), &pid_attr);
if (ret) {
- dev_err(disk_to_dev(lo->disk), "device_create_file failed!\n");
- lo->pid = 0;
+ dev_err(disk_to_dev(nbd->disk), "device_create_file failed!\n");
+ nbd->pid = 0;
return ret;
}
- while ((req = nbd_read_stat(lo)) != NULL)
+ while ((req = nbd_read_stat(nbd)) != NULL)
nbd_end_request(req);
- device_remove_file(disk_to_dev(lo->disk), &pid_attr);
- lo->pid = 0;
+ device_remove_file(disk_to_dev(nbd->disk), &pid_attr);
+ nbd->pid = 0;
return 0;
}
-static void nbd_clear_que(struct nbd_device *lo)
+static void nbd_clear_que(struct nbd_device *nbd)
{
struct request *req;
- BUG_ON(lo->magic != LO_MAGIC);
+ BUG_ON(nbd->magic != NBD_MAGIC);
/*
- * Because we have set lo->sock to NULL under the tx_lock, all
+ * Because we have set nbd->sock to NULL under the tx_lock, all
* modifications to the list must have completed by now. For
* the same reason, the active_req must be NULL.
*
* As a consequence, we don't need to take the spin lock while
* purging the list here.
*/
- BUG_ON(lo->sock);
- BUG_ON(lo->active_req);
+ BUG_ON(nbd->sock);
+ BUG_ON(nbd->active_req);
- while (!list_empty(&lo->queue_head)) {
- req = list_entry(lo->queue_head.next, struct request,
+ while (!list_empty(&nbd->queue_head)) {
+ req = list_entry(nbd->queue_head.next, struct request,
queuelist);
list_del_init(&req->queuelist);
req->errors++;
@@ -448,7 +448,7 @@ static void nbd_clear_que(struct nbd_device *lo)
}
-static void nbd_handle_req(struct nbd_device *lo, struct request *req)
+static void nbd_handle_req(struct nbd_device *nbd, struct request *req)
{
if (req->cmd_type != REQ_TYPE_FS)
goto error_out;
@@ -456,8 +456,8 @@ static void nbd_handle_req(struct nbd_device *lo, struct request *req)
nbd_cmd(req) = NBD_CMD_READ;
if (rq_data_dir(req) == WRITE) {
nbd_cmd(req) = NBD_CMD_WRITE;
- if (lo->flags & NBD_READ_ONLY) {
- dev_err(disk_to_dev(lo->disk),
+ if (nbd->flags & NBD_READ_ONLY) {
+ dev_err(disk_to_dev(nbd->disk),
"Write on read-only\n");
goto error_out;
}
@@ -465,29 +465,29 @@ static void nbd_handle_req(struct nbd_device *lo, struct request *req)
req->errors = 0;
- mutex_lock(&lo->tx_lock);
- if (unlikely(!lo->sock)) {
- mutex_unlock(&lo->tx_lock);
- dev_err(disk_to_dev(lo->disk),
+ mutex_lock(&nbd->tx_lock);
+ if (unlikely(!nbd->sock)) {
+ mutex_unlock(&nbd->tx_lock);
+ dev_err(disk_to_dev(nbd->disk),
"Attempted send on closed socket\n");
goto error_out;
}
- lo->active_req = req;
+ nbd->active_req = req;
- if (nbd_send_req(lo, req) != 0) {
- dev_err(disk_to_dev(lo->disk), "Request send failed\n");
+ if (nbd_send_req(nbd, req) != 0) {
+ dev_err(disk_to_dev(nbd->disk), "Request send failed\n");
req->errors++;
nbd_end_request(req);
} else {
- spin_lock(&lo->queue_lock);
- list_add(&req->queuelist, &lo->queue_head);
- spin_unlock(&lo->queue_lock);
+ spin_lock(&nbd->queue_lock);
+ list_add(&req->queuelist, &nbd->queue_head);
+ spin_unlock(&nbd->queue_lock);
}
- lo->active_req = NULL;
- mutex_unlock(&lo->tx_lock);
- wake_up_all(&lo->active_wq);
+ nbd->active_req = NULL;
+ mutex_unlock(&nbd->tx_lock);
+ wake_up_all(&nbd->active_wq);
return;
@@ -498,28 +498,28 @@ error_out:
static int nbd_thread(void *data)
{
- struct nbd_device *lo = data;
+ struct nbd_device *nbd = data;
struct request *req;
set_user_nice(current, -20);
- while (!kthread_should_stop() || !list_empty(&lo->waiting_queue)) {
+ while (!kthread_should_stop() || !list_empty(&nbd->waiting_queue)) {
/* wait for something to do */
- wait_event_interruptible(lo->waiting_wq,
+ wait_event_interruptible(nbd->waiting_wq,
kthread_should_stop() ||
- !list_empty(&lo->waiting_queue));
+ !list_empty(&nbd->waiting_queue));
/* extract request */
- if (list_empty(&lo->waiting_queue))
+ if (list_empty(&nbd->waiting_queue))
continue;
- spin_lock_irq(&lo->queue_lock);
- req = list_entry(lo->waiting_queue.next, struct request,
+ spin_lock_irq(&nbd->queue_lock);
+ req = list_entry(nbd->waiting_queue.next, struct request,
queuelist);
list_del_init(&req->queuelist);
- spin_unlock_irq(&lo->queue_lock);
+ spin_unlock_irq(&nbd->queue_lock);
/* handle request */
- nbd_handle_req(lo, req);
+ nbd_handle_req(nbd, req);
}
return 0;
}
@@ -527,7 +527,7 @@ static int nbd_thread(void *data)
/*
* We always wait for result of write, for now. It would be nice to make it optional
* in future
- * if ((rq_data_dir(req) == WRITE) && (lo->flags & NBD_WRITE_NOCHK))
+ * if ((rq_data_dir(req) == WRITE) && (nbd->flags & NBD_WRITE_NOCHK))
* { printk( "Warning: Ignoring result!\n"); nbd_end_request( req ); }
*/
@@ -536,19 +536,19 @@ static void do_nbd_request(struct request_queue *q)
struct request *req;
while ((req = blk_fetch_request(q)) != NULL) {
- struct nbd_device *lo;
+ struct nbd_device *nbd;
spin_unlock_irq(q->queue_lock);
dprintk(DBG_BLKDEV, "%s: request %p: dequeued (flags=%x)\n",
req->rq_disk->disk_name, req, req->cmd_type);
- lo = req->rq_disk->private_data;
+ nbd = req->rq_disk->private_data;
- BUG_ON(lo->magic != LO_MAGIC);
+ BUG_ON(nbd->magic != NBD_MAGIC);
- if (unlikely(!lo->sock)) {
- dev_err(disk_to_dev(lo->disk),
+ if (unlikely(!nbd->sock)) {
+ dev_err(disk_to_dev(nbd->disk),
"Attempted send on closed socket\n");
req->errors++;
nbd_end_request(req);
@@ -556,11 +556,11 @@ static void do_nbd_request(struct request_queue *q)
continue;
}
- spin_lock_irq(&lo->queue_lock);
- list_add_tail(&req->queuelist, &lo->waiting_queue);
- spin_unlock_irq(&lo->queue_lock);
+ spin_lock_irq(&nbd->queue_lock);
+ list_add_tail(&req->queuelist, &nbd->waiting_queue);
+ spin_unlock_irq(&nbd->queue_lock);
- wake_up(&lo->waiting_wq);
+ wake_up(&nbd->waiting_wq);
spin_lock_irq(q->queue_lock);
}
@@ -568,32 +568,32 @@ static void do_nbd_request(struct request_queue *q)
/* Must be called with tx_lock held */
-static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
+static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case NBD_DISCONNECT: {
struct request sreq;
- dev_info(disk_to_dev(lo->disk), "NBD_DISCONNECT\n");
+ dev_info(disk_to_dev(nbd->disk), "NBD_DISCONNECT\n");
blk_rq_init(NULL, &sreq);
sreq.cmd_type = REQ_TYPE_SPECIAL;
nbd_cmd(&sreq) = NBD_CMD_DISC;
- if (!lo->sock)
+ if (!nbd->sock)
return -EINVAL;
- nbd_send_req(lo, &sreq);
+ nbd_send_req(nbd, &sreq);
return 0;
}
case NBD_CLEAR_SOCK: {
struct file *file;
- lo->sock = NULL;
- file = lo->file;
- lo->file = NULL;
- nbd_clear_que(lo);
- BUG_ON(!list_empty(&lo->queue_head));
+ nbd->sock = NULL;
+ file = nbd->file;
+ nbd->file = NULL;
+ nbd_clear_que(nbd);
+ BUG_ON(!list_empty(&nbd->queue_head));
if (file)
fput(file);
return 0;
@@ -601,14 +601,14 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
case NBD_SET_SOCK: {
struct file *file;
- if (lo->file)
+ if (nbd->file)
return -EBUSY;
file = fget(arg);
if (file) {
struct inode *inode = file->f_path.dentry->d_inode;
if (S_ISSOCK(inode->i_mode)) {
- lo->file = file;
- lo->sock = SOCKET_I(inode);
+ nbd->file = file;
+ nbd->sock = SOCKET_I(inode);
if (max_part > 0)
bdev->bd_invalidated = 1;
return 0;
@@ -620,29 +620,29 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
}
case NBD_SET_BLKSIZE:
- lo->blksize = arg;
- lo->bytesize &= ~(lo->blksize-1);
- bdev->bd_inode->i_size = lo->bytesize;
- set_blocksize(bdev, lo->blksize);
- set_capacity(lo->disk, lo->bytesize >> 9);
+ nbd->blksize = arg;
+ nbd->bytesize &= ~(nbd->blksize-1);
+ bdev->bd_inode->i_size = nbd->bytesize;
+ set_blocksize(bdev, nbd->blksize);
+ set_capacity(nbd->disk, nbd->bytesize >> 9);
return 0;
case NBD_SET_SIZE:
- lo->bytesize = arg & ~(lo->blksize-1);
- bdev->bd_inode->i_size = lo->bytesize;
- set_blocksize(bdev, lo->blksize);
- set_capacity(lo->disk, lo->bytesize >> 9);
+ nbd->bytesize = arg & ~(nbd->blksize-1);
+ bdev->bd_inode->i_size = nbd->bytesize;
+ set_blocksize(bdev, nbd->blksize);
+ set_capacity(nbd->disk, nbd->bytesize >> 9);
return 0;
case NBD_SET_TIMEOUT:
- lo->xmit_timeout = arg * HZ;
+ nbd->xmit_timeout = arg * HZ;
return 0;
case NBD_SET_SIZE_BLOCKS:
- lo->bytesize = ((u64) arg) * lo->blksize;
- bdev->bd_inode->i_size = lo->bytesize;
- set_blocksize(bdev, lo->blksize);
- set_capacity(lo->disk, lo->bytesize >> 9);
+ nbd->bytesize = ((u64) arg) * nbd->blksize;
+ bdev->bd_inode->i_size = nbd->bytesize;
+ set_blocksize(bdev, nbd->blksize);
+ set_capacity(nbd->disk, nbd->bytesize >> 9);
return 0;
case NBD_DO_IT: {
@@ -650,38 +650,38 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
struct file *file;
int error;
- if (lo->pid)
+ if (nbd->pid)
return -EBUSY;
- if (!lo->file)
+ if (!nbd->file)
return -EINVAL;
- mutex_unlock(&lo->tx_lock);
+ mutex_unlock(&nbd->tx_lock);
- thread = kthread_create(nbd_thread, lo, lo->disk->disk_name);
+ thread = kthread_create(nbd_thread, nbd, nbd->disk->disk_name);
if (IS_ERR(thread)) {
- mutex_lock(&lo->tx_lock);
+ mutex_lock(&nbd->tx_lock);
return PTR_ERR(thread);
}
wake_up_process(thread);
- error = nbd_do_it(lo);
+ error = nbd_do_it(nbd);
kthread_stop(thread);
- mutex_lock(&lo->tx_lock);
+ mutex_lock(&nbd->tx_lock);
if (error)
return error;
- sock_shutdown(lo, 0);
- file = lo->file;
- lo->file = NULL;
- nbd_clear_que(lo);
- dev_warn(disk_to_dev(lo->disk), "queue cleared\n");
+ sock_shutdown(nbd, 0);
+ file = nbd->file;
+ nbd->file = NULL;
+ nbd_clear_que(nbd);
+ dev_warn(disk_to_dev(nbd->disk), "queue cleared\n");
if (file)
fput(file);
- lo->bytesize = 0;
+ nbd->bytesize = 0;
bdev->bd_inode->i_size = 0;
- set_capacity(lo->disk, 0);
+ set_capacity(nbd->disk, 0);
if (max_part > 0)
ioctl_by_bdev(bdev, BLKRRPART, 0);
- return lo->harderror;
+ return nbd->harderror;
}
case NBD_CLEAR_QUE:
@@ -689,14 +689,14 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
* This is for compatibility only. The queue is always cleared
* by NBD_DO_IT or NBD_CLEAR_SOCK.
*/
- BUG_ON(!lo->sock && !list_empty(&lo->queue_head));
+ BUG_ON(!nbd->sock && !list_empty(&nbd->queue_head));
return 0;
case NBD_PRINT_DEBUG:
- dev_info(disk_to_dev(lo->disk),
+ dev_info(disk_to_dev(nbd->disk),
"next = %p, prev = %p, head = %p\n",
- lo->queue_head.next, lo->queue_head.prev,
- &lo->queue_head);
+ nbd->queue_head.next, nbd->queue_head.prev,
+ &nbd->queue_head);
return 0;
}
return -ENOTTY;
@@ -705,21 +705,21 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo,
static int nbd_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
- struct nbd_device *lo = bdev->bd_disk->private_data;
+ struct nbd_device *nbd = bdev->bd_disk->private_data;
int error;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- BUG_ON(lo->magic != LO_MAGIC);
+ BUG_ON(nbd->magic != NBD_MAGIC);
/* Anyone capable of this syscall can do *real bad* things */
dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n",
- lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg);
+ nbd->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg);
- mutex_lock(&lo->tx_lock);
- error = __nbd_ioctl(bdev, lo, cmd, arg);
- mutex_unlock(&lo->tx_lock);
+ mutex_lock(&nbd->tx_lock);
+ error = __nbd_ioctl(bdev, nbd, cmd, arg);
+ mutex_unlock(&nbd->tx_lock);
return error;
}
@@ -805,7 +805,7 @@ static int __init nbd_init(void)
for (i = 0; i < nbds_max; i++) {
struct gendisk *disk = nbd_dev[i].disk;
nbd_dev[i].file = NULL;
- nbd_dev[i].magic = LO_MAGIC;
+ nbd_dev[i].magic = NBD_MAGIC;
nbd_dev[i].flags = 0;
INIT_LIST_HEAD(&nbd_dev[i].waiting_queue);
spin_lock_init(&nbd_dev[i].queue_lock);
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index a6278e7..013c7a5 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -41,19 +41,35 @@
#include "rbd_types.h"
-#define DRV_NAME "rbd"
-#define DRV_NAME_LONG "rbd (rados block device)"
+/*
+ * The basic unit of block I/O is a sector. It is interpreted in a
+ * number of contexts in Linux (blk, bio, genhd), but the default is
+ * universally 512 bytes. These symbols are just slightly more
+ * meaningful than the bare numbers they represent.
+ */
+#define SECTOR_SHIFT 9
+#define SECTOR_SIZE (1ULL << SECTOR_SHIFT)
+
+#define RBD_DRV_NAME "rbd"
+#define RBD_DRV_NAME_LONG "rbd (rados block device)"
#define RBD_MINORS_PER_MAJOR 256 /* max minors per blkdev */
-#define RBD_MAX_MD_NAME_LEN (96 + sizeof(RBD_SUFFIX))
+#define RBD_MAX_MD_NAME_LEN (RBD_MAX_OBJ_NAME_LEN + sizeof(RBD_SUFFIX))
#define RBD_MAX_POOL_NAME_LEN 64
#define RBD_MAX_SNAP_NAME_LEN 32
#define RBD_MAX_OPT_LEN 1024
#define RBD_SNAP_HEAD_NAME "-"
+/*
+ * An RBD device name will be "rbd#", where the "rbd" comes from
+ * RBD_DRV_NAME above, and # is a unique integer identifier.
+ * MAX_INT_FORMAT_WIDTH is used in ensuring DEV_NAME_LEN is big
+ * enough to hold all possible device names.
+ */
#define DEV_NAME_LEN 32
+#define MAX_INT_FORMAT_WIDTH ((5 * sizeof (int)) / 2 + 1)
#define RBD_NOTIFY_TIMEOUT_DEFAULT 10
@@ -66,7 +82,6 @@ struct rbd_image_header {
__u8 obj_order;
__u8 crypt_type;
__u8 comp_type;
- struct rw_semaphore snap_rwsem;
struct ceph_snap_context *snapc;
size_t snap_names_len;
u64 snap_seq;
@@ -83,7 +98,7 @@ struct rbd_options {
};
/*
- * an instance of the client. multiple devices may share a client.
+ * an instance of the client. multiple devices may share an rbd client.
*/
struct rbd_client {
struct ceph_client *client;
@@ -92,20 +107,9 @@ struct rbd_client {
struct list_head node;
};
-struct rbd_req_coll;
-
/*
- * a single io request
+ * a request completion status
*/
-struct rbd_request {
- struct request *rq; /* blk layer request */
- struct bio *bio; /* cloned bio */
- struct page **pages; /* list of used pages */
- u64 len;
- int coll_index;
- struct rbd_req_coll *coll;
-};
-
struct rbd_req_status {
int done;
int rc;
@@ -122,6 +126,18 @@ struct rbd_req_coll {
struct rbd_req_status status[0];
};
+/*
+ * a single io request
+ */
+struct rbd_request {
+ struct request *rq; /* blk layer request */
+ struct bio *bio; /* cloned bio */
+ struct page **pages; /* list of used pages */
+ u64 len;
+ int coll_index;
+ struct rbd_req_coll *coll;
+};
+
struct rbd_snap {
struct device dev;
const char *name;
@@ -140,7 +156,6 @@ struct rbd_device {
struct gendisk *disk; /* blkdev's gendisk and rq */
struct request_queue *q;
- struct ceph_client *client;
struct rbd_client *rbd_client;
char name[DEV_NAME_LEN]; /* blkdev name, e.g. rbd3 */
@@ -157,6 +172,8 @@ struct rbd_device {
struct ceph_osd_event *watch_event;
struct ceph_osd_request *watch_request;
+ /* protects updating the header */
+ struct rw_semaphore header_rwsem;
char snap_name[RBD_MAX_SNAP_NAME_LEN];
u32 cur_snap; /* index+1 of current snapshot within snap context
0 - for the head */
@@ -171,15 +188,13 @@ struct rbd_device {
struct device dev;
};
-static struct bus_type rbd_bus_type = {
- .name = "rbd",
-};
-
-static spinlock_t node_lock; /* protects client get/put */
-
static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */
+
static LIST_HEAD(rbd_dev_list); /* devices */
-static LIST_HEAD(rbd_client_list); /* clients */
+static DEFINE_SPINLOCK(rbd_dev_list_lock);
+
+static LIST_HEAD(rbd_client_list); /* clients */
+static DEFINE_SPINLOCK(rbd_client_list_lock);
static int __rbd_init_snaps_header(struct rbd_device *rbd_dev);
static void rbd_dev_release(struct device *dev);
@@ -190,12 +205,32 @@ static ssize_t rbd_snap_add(struct device *dev,
static void __rbd_remove_snap_dev(struct rbd_device *rbd_dev,
struct rbd_snap *snap);
+static ssize_t rbd_add(struct bus_type *bus, const char *buf,
+ size_t count);
+static ssize_t rbd_remove(struct bus_type *bus, const char *buf,
+ size_t count);
-static struct rbd_device *dev_to_rbd(struct device *dev)
+static struct bus_attribute rbd_bus_attrs[] = {
+ __ATTR(add, S_IWUSR, NULL, rbd_add),
+ __ATTR(remove, S_IWUSR, NULL, rbd_remove),
+ __ATTR_NULL
+};
+
+static struct bus_type rbd_bus_type = {
+ .name = "rbd",
+ .bus_attrs = rbd_bus_attrs,
+};
+
+static void rbd_root_dev_release(struct device *dev)
{
- return container_of(dev, struct rbd_device, dev);
}
+static struct device rbd_root_dev = {
+ .init_name = "rbd",
+ .release = rbd_root_dev_release,
+};
+
+
static struct device *rbd_get_dev(struct rbd_device *rbd_dev)
{
return get_device(&rbd_dev->dev);
@@ -210,8 +245,7 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev);
static int rbd_open(struct block_device *bdev, fmode_t mode)
{
- struct gendisk *disk = bdev->bd_disk;
- struct rbd_device *rbd_dev = disk->private_data;
+ struct rbd_device *rbd_dev = bdev->bd_disk->private_data;
rbd_get_dev(rbd_dev);
@@ -256,9 +290,11 @@ static struct rbd_client *rbd_client_create(struct ceph_options *opt,
kref_init(&rbdc->kref);
INIT_LIST_HEAD(&rbdc->node);
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+
rbdc->client = ceph_create_client(opt, rbdc, 0, 0);
if (IS_ERR(rbdc->client))
- goto out_rbdc;
+ goto out_mutex;
opt = NULL; /* Now rbdc->client is responsible for opt */
ret = ceph_open_session(rbdc->client);
@@ -267,16 +303,19 @@ static struct rbd_client *rbd_client_create(struct ceph_options *opt,
rbdc->rbd_opts = rbd_opts;
- spin_lock(&node_lock);
+ spin_lock(&rbd_client_list_lock);
list_add_tail(&rbdc->node, &rbd_client_list);
- spin_unlock(&node_lock);
+ spin_unlock(&rbd_client_list_lock);
+
+ mutex_unlock(&ctl_mutex);
dout("rbd_client_create created %p\n", rbdc);
return rbdc;
out_err:
ceph_destroy_client(rbdc->client);
-out_rbdc:
+out_mutex:
+ mutex_unlock(&ctl_mutex);
kfree(rbdc);
out_opt:
if (opt)
@@ -324,7 +363,7 @@ static int parse_rbd_opts_token(char *c, void *private)
substring_t argstr[MAX_OPT_ARGS];
int token, intval, ret;
- token = match_token((char *)c, rbdopt_tokens, argstr);
+ token = match_token(c, rbdopt_tokens, argstr);
if (token < 0)
return -EINVAL;
@@ -357,58 +396,54 @@ static int parse_rbd_opts_token(char *c, void *private)
* Get a ceph client with specific addr and configuration, if one does
* not exist create it.
*/
-static int rbd_get_client(struct rbd_device *rbd_dev, const char *mon_addr,
- char *options)
+static struct rbd_client *rbd_get_client(const char *mon_addr,
+ size_t mon_addr_len,
+ char *options)
{
struct rbd_client *rbdc;
struct ceph_options *opt;
- int ret;
struct rbd_options *rbd_opts;
rbd_opts = kzalloc(sizeof(*rbd_opts), GFP_KERNEL);
if (!rbd_opts)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
rbd_opts->notify_timeout = RBD_NOTIFY_TIMEOUT_DEFAULT;
- ret = ceph_parse_options(&opt, options, mon_addr,
- mon_addr + strlen(mon_addr), parse_rbd_opts_token, rbd_opts);
- if (ret < 0)
- goto done_err;
+ opt = ceph_parse_options(options, mon_addr,
+ mon_addr + mon_addr_len,
+ parse_rbd_opts_token, rbd_opts);
+ if (IS_ERR(opt)) {
+ kfree(rbd_opts);
+ return ERR_CAST(opt);
+ }
- spin_lock(&node_lock);
+ spin_lock(&rbd_client_list_lock);
rbdc = __rbd_client_find(opt);
if (rbdc) {
+ /* using an existing client */
+ kref_get(&rbdc->kref);
+ spin_unlock(&rbd_client_list_lock);
+
ceph_destroy_options(opt);
kfree(rbd_opts);
- /* using an existing client */
- kref_get(&rbdc->kref);
- rbd_dev->rbd_client = rbdc;
- rbd_dev->client = rbdc->client;
- spin_unlock(&node_lock);
- return 0;
+ return rbdc;
}
- spin_unlock(&node_lock);
+ spin_unlock(&rbd_client_list_lock);
rbdc = rbd_client_create(opt, rbd_opts);
- if (IS_ERR(rbdc)) {
- ret = PTR_ERR(rbdc);
- goto done_err;
- }
- rbd_dev->rbd_client = rbdc;
- rbd_dev->client = rbdc->client;
- return 0;
-done_err:
- kfree(rbd_opts);
- return ret;
+ if (IS_ERR(rbdc))
+ kfree(rbd_opts);
+
+ return rbdc;
}
/*
* Destroy ceph client
*
- * Caller must hold node_lock.
+ * Caller must hold rbd_client_list_lock.
*/
static void rbd_client_release(struct kref *kref)
{
@@ -428,11 +463,10 @@ static void rbd_client_release(struct kref *kref)
*/
static void rbd_put_client(struct rbd_device *rbd_dev)
{
- spin_lock(&node_lock);
+ spin_lock(&rbd_client_list_lock);
kref_put(&rbd_dev->rbd_client->kref, rbd_client_release);
- spin_unlock(&node_lock);
+ spin_unlock(&rbd_client_list_lock);
rbd_dev->rbd_client = NULL;
- rbd_dev->client = NULL;
}
/*
@@ -457,21 +491,19 @@ static int rbd_header_from_disk(struct rbd_image_header *header,
gfp_t gfp_flags)
{
int i;
- u32 snap_count = le32_to_cpu(ondisk->snap_count);
- int ret = -ENOMEM;
+ u32 snap_count;
- if (memcmp(ondisk, RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT))) {
+ if (memcmp(ondisk, RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT)))
return -ENXIO;
- }
- init_rwsem(&header->snap_rwsem);
- header->snap_names_len = le64_to_cpu(ondisk->snap_names_len);
+ snap_count = le32_to_cpu(ondisk->snap_count);
header->snapc = kmalloc(sizeof(struct ceph_snap_context) +
- snap_count *
- sizeof(struct rbd_image_snap_ondisk),
+ snap_count * sizeof (*ondisk),
gfp_flags);
if (!header->snapc)
return -ENOMEM;
+
+ header->snap_names_len = le64_to_cpu(ondisk->snap_names_len);
if (snap_count) {
header->snap_names = kmalloc(header->snap_names_len,
GFP_KERNEL);
@@ -498,8 +530,7 @@ static int rbd_header_from_disk(struct rbd_image_header *header,
header->snapc->num_snaps = snap_count;
header->total_snaps = snap_count;
- if (snap_count &&
- allocated_snaps == snap_count) {
+ if (snap_count && allocated_snaps == snap_count) {
for (i = 0; i < snap_count; i++) {
header->snapc->snaps[i] =
le64_to_cpu(ondisk->snaps[i].id);
@@ -518,7 +549,7 @@ err_names:
kfree(header->snap_names);
err_snapc:
kfree(header->snapc);
- return ret;
+ return -ENOMEM;
}
static int snap_index(struct rbd_image_header *header, int snap_num)
@@ -542,35 +573,34 @@ static int snap_by_name(struct rbd_image_header *header, const char *snap_name,
int i;
char *p = header->snap_names;
- for (i = 0; i < header->total_snaps; i++, p += strlen(p) + 1) {
- if (strcmp(snap_name, p) == 0)
- break;
- }
- if (i == header->total_snaps)
- return -ENOENT;
- if (seq)
- *seq = header->snapc->snaps[i];
+ for (i = 0; i < header->total_snaps; i++) {
+ if (!strcmp(snap_name, p)) {
- if (size)
- *size = header->snap_sizes[i];
+ /* Found it. Pass back its id and/or size */
- return i;
+ if (seq)
+ *seq = header->snapc->snaps[i];
+ if (size)
+ *size = header->snap_sizes[i];
+ return i;
+ }
+ p += strlen(p) + 1; /* Skip ahead to the next name */
+ }
+ return -ENOENT;
}
-static int rbd_header_set_snap(struct rbd_device *dev,
- const char *snap_name,
- u64 *size)
+static int rbd_header_set_snap(struct rbd_device *dev, u64 *size)
{
struct rbd_image_header *header = &dev->header;
struct ceph_snap_context *snapc = header->snapc;
int ret = -ENOENT;
- down_write(&header->snap_rwsem);
+ BUILD_BUG_ON(sizeof (dev->snap_name) < sizeof (RBD_SNAP_HEAD_NAME));
- if (!snap_name ||
- !*snap_name ||
- strcmp(snap_name, "-") == 0 ||
- strcmp(snap_name, RBD_SNAP_HEAD_NAME) == 0) {
+ down_write(&dev->header_rwsem);
+
+ if (!memcmp(dev->snap_name, RBD_SNAP_HEAD_NAME,
+ sizeof (RBD_SNAP_HEAD_NAME))) {
if (header->total_snaps)
snapc->seq = header->snap_seq;
else
@@ -580,7 +610,7 @@ static int rbd_header_set_snap(struct rbd_device *dev,
if (size)
*size = header->image_size;
} else {
- ret = snap_by_name(header, snap_name, &snapc->seq, size);
+ ret = snap_by_name(header, dev->snap_name, &snapc->seq, size);
if (ret < 0)
goto done;
@@ -590,7 +620,7 @@ static int rbd_header_set_snap(struct rbd_device *dev,
ret = 0;
done:
- up_write(&header->snap_rwsem);
+ up_write(&dev->header_rwsem);
return ret;
}
@@ -717,7 +747,7 @@ static struct bio *bio_chain_clone(struct bio **old, struct bio **next,
/* split the bio. We'll release it either in the next
call, or it will have to be released outside */
- bp = bio_split(old_chain, (len - total) / 512ULL);
+ bp = bio_split(old_chain, (len - total) / SECTOR_SIZE);
if (!bp)
goto err_out;
@@ -857,7 +887,7 @@ static int rbd_do_request(struct request *rq,
struct timespec mtime = CURRENT_TIME;
struct rbd_request *req_data;
struct ceph_osd_request_head *reqhead;
- struct rbd_image_header *header = &dev->header;
+ struct ceph_osd_client *osdc;
req_data = kzalloc(sizeof(*req_data), GFP_NOIO);
if (!req_data) {
@@ -874,15 +904,13 @@ static int rbd_do_request(struct request *rq,
dout("rbd_do_request obj=%s ofs=%lld len=%lld\n", obj, len, ofs);
- down_read(&header->snap_rwsem);
+ down_read(&dev->header_rwsem);
- req = ceph_osdc_alloc_request(&dev->client->osdc, flags,
- snapc,
- ops,
- false,
- GFP_NOIO, pages, bio);
+ osdc = &dev->rbd_client->client->osdc;
+ req = ceph_osdc_alloc_request(osdc, flags, snapc, ops,
+ false, GFP_NOIO, pages, bio);
if (!req) {
- up_read(&header->snap_rwsem);
+ up_read(&dev->header_rwsem);
ret = -ENOMEM;
goto done_pages;
}
@@ -909,27 +937,27 @@ static int rbd_do_request(struct request *rq,
layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER);
layout->fl_pg_preferred = cpu_to_le32(-1);
layout->fl_pg_pool = cpu_to_le32(dev->poolid);
- ceph_calc_raw_layout(&dev->client->osdc, layout, snapid,
- ofs, &len, &bno, req, ops);
+ ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno,
+ req, ops);
ceph_osdc_build_request(req, ofs, &len,
ops,
snapc,
&mtime,
req->r_oid, req->r_oid_len);
- up_read(&header->snap_rwsem);
+ up_read(&dev->header_rwsem);
if (linger_req) {
- ceph_osdc_set_request_linger(&dev->client->osdc, req);
+ ceph_osdc_set_request_linger(osdc, req);
*linger_req = req;
}
- ret = ceph_osdc_start_request(&dev->client->osdc, req, false);
+ ret = ceph_osdc_start_request(osdc, req, false);
if (ret < 0)
goto done_err;
if (!rbd_cb) {
- ret = ceph_osdc_wait_request(&dev->client->osdc, req);
+ ret = ceph_osdc_wait_request(osdc, req);
if (ver)
*ver = le64_to_cpu(req->r_reassert_version.version);
dout("reassert_ver=%lld\n",
@@ -1213,8 +1241,8 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
rc = __rbd_update_snaps(dev);
mutex_unlock(&ctl_mutex);
if (rc)
- pr_warning(DRV_NAME "%d got notification but failed to update"
- " snaps: %d\n", dev->major, rc);
+ pr_warning(RBD_DRV_NAME "%d got notification but failed to "
+ " update snaps: %d\n", dev->major, rc);
rbd_req_sync_notify_ack(dev, ver, notify_id, dev->obj_md_name);
}
@@ -1227,7 +1255,7 @@ static int rbd_req_sync_watch(struct rbd_device *dev,
u64 ver)
{
struct ceph_osd_req_op *ops;
- struct ceph_osd_client *osdc = &dev->client->osdc;
+ struct ceph_osd_client *osdc = &dev->rbd_client->client->osdc;
int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0);
if (ret < 0)
@@ -1314,7 +1342,7 @@ static int rbd_req_sync_notify(struct rbd_device *dev,
const char *obj)
{
struct ceph_osd_req_op *ops;
- struct ceph_osd_client *osdc = &dev->client->osdc;
+ struct ceph_osd_client *osdc = &dev->rbd_client->client->osdc;
struct ceph_osd_event *event;
struct rbd_notify_info info;
int payload_len = sizeof(u32) + sizeof(u32);
@@ -1421,9 +1449,7 @@ static void rbd_rq_fn(struct request_queue *q)
struct request *rq;
struct bio_pair *bp = NULL;
- rq = blk_fetch_request(q);
-
- while (1) {
+ while ((rq = blk_fetch_request(q))) {
struct bio *bio;
struct bio *rq_bio, *next_bio = NULL;
bool do_write;
@@ -1441,32 +1467,32 @@ static void rbd_rq_fn(struct request_queue *q)
/* filter out block requests we don't understand */
if ((rq->cmd_type != REQ_TYPE_FS)) {
__blk_end_request_all(rq, 0);
- goto next;
+ continue;
}
/* deduce our operation (read, write) */
do_write = (rq_data_dir(rq) == WRITE);
size = blk_rq_bytes(rq);
- ofs = blk_rq_pos(rq) * 512ULL;
+ ofs = blk_rq_pos(rq) * SECTOR_SIZE;
rq_bio = rq->bio;
if (do_write && rbd_dev->read_only) {
__blk_end_request_all(rq, -EROFS);
- goto next;
+ continue;
}
spin_unlock_irq(q->queue_lock);
dout("%s 0x%x bytes at 0x%llx\n",
do_write ? "write" : "read",
- size, blk_rq_pos(rq) * 512ULL);
+ size, blk_rq_pos(rq) * SECTOR_SIZE);
num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size);
coll = rbd_alloc_coll(num_segs);
if (!coll) {
spin_lock_irq(q->queue_lock);
__blk_end_request_all(rq, -ENOMEM);
- goto next;
+ continue;
}
do {
@@ -1512,8 +1538,6 @@ next_seg:
if (bp)
bio_pair_release(bp);
spin_lock_irq(q->queue_lock);
-next:
- rq = blk_fetch_request(q);
}
}
@@ -1526,13 +1550,17 @@ static int rbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bmd,
struct bio_vec *bvec)
{
struct rbd_device *rbd_dev = q->queuedata;
- unsigned int chunk_sectors = 1 << (rbd_dev->header.obj_order - 9);
- sector_t sector = bmd->bi_sector + get_start_sect(bmd->bi_bdev);
- unsigned int bio_sectors = bmd->bi_size >> 9;
+ unsigned int chunk_sectors;
+ sector_t sector;
+ unsigned int bio_sectors;
int max;
+ chunk_sectors = 1 << (rbd_dev->header.obj_order - SECTOR_SHIFT);
+ sector = bmd->bi_sector + get_start_sect(bmd->bi_bdev);
+ bio_sectors = bmd->bi_size >> SECTOR_SHIFT;
+
max = (chunk_sectors - ((sector & (chunk_sectors - 1))
- + bio_sectors)) << 9;
+ + bio_sectors)) << SECTOR_SHIFT;
if (max < 0)
max = 0; /* bio_add cannot handle a negative return */
if (max <= bvec->bv_len && bio_sectors == 0)
@@ -1565,15 +1593,16 @@ static int rbd_read_header(struct rbd_device *rbd_dev,
ssize_t rc;
struct rbd_image_header_ondisk *dh;
int snap_count = 0;
- u64 snap_names_len = 0;
u64 ver;
+ size_t len;
+ /*
+ * First reads the fixed-size header to determine the number
+ * of snapshots, then re-reads it, along with all snapshot
+ * records as well as their stored names.
+ */
+ len = sizeof (*dh);
while (1) {
- int len = sizeof(*dh) +
- snap_count * sizeof(struct rbd_image_snap_ondisk) +
- snap_names_len;
-
- rc = -ENOMEM;
dh = kmalloc(len, GFP_KERNEL);
if (!dh)
return -ENOMEM;
@@ -1588,21 +1617,22 @@ static int rbd_read_header(struct rbd_device *rbd_dev,
rc = rbd_header_from_disk(header, dh, snap_count, GFP_KERNEL);
if (rc < 0) {
- if (rc == -ENXIO) {
+ if (rc == -ENXIO)
pr_warning("unrecognized header format"
" for image %s", rbd_dev->obj);
- }
goto out_dh;
}
- if (snap_count != header->total_snaps) {
- snap_count = header->total_snaps;
- snap_names_len = header->snap_names_len;
- rbd_header_free(header);
- kfree(dh);
- continue;
- }
- break;
+ if (snap_count == header->total_snaps)
+ break;
+
+ snap_count = header->total_snaps;
+ len = sizeof (*dh) +
+ snap_count * sizeof(struct rbd_image_snap_ondisk) +
+ header->snap_names_len;
+
+ rbd_header_free(header);
+ kfree(dh);
}
header->obj_version = ver;
@@ -1623,13 +1653,14 @@ static int rbd_header_add_snap(struct rbd_device *dev,
int ret;
void *data, *p, *e;
u64 ver;
+ struct ceph_mon_client *monc;
/* we should create a snapshot only if we're pointing at the head */
if (dev->cur_snap)
return -EINVAL;
- ret = ceph_monc_create_snapid(&dev->client->monc, dev->poolid,
- &new_snapid);
+ monc = &dev->rbd_client->client->monc;
+ ret = ceph_monc_create_snapid(monc, dev->poolid, &new_snapid);
dout("created snapid=%lld\n", new_snapid);
if (ret < 0)
return ret;
@@ -1684,9 +1715,9 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev)
return ret;
/* resized? */
- set_capacity(rbd_dev->disk, h.image_size / 512ULL);
+ set_capacity(rbd_dev->disk, h.image_size / SECTOR_SIZE);
- down_write(&rbd_dev->header.snap_rwsem);
+ down_write(&rbd_dev->header_rwsem);
snap_seq = rbd_dev->header.snapc->seq;
if (rbd_dev->header.total_snaps &&
@@ -1711,7 +1742,7 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev)
ret = __rbd_init_snaps_header(rbd_dev);
- up_write(&rbd_dev->header.snap_rwsem);
+ up_write(&rbd_dev->header_rwsem);
return ret;
}
@@ -1721,6 +1752,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
struct gendisk *disk;
struct request_queue *q;
int rc;
+ u64 segment_size;
u64 total_size = 0;
/* contact OSD, request size info about the object being mapped */
@@ -1733,7 +1765,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
if (rc)
return rc;
- rc = rbd_header_set_snap(rbd_dev, rbd_dev->snap_name, &total_size);
+ rc = rbd_header_set_snap(rbd_dev, &total_size);
if (rc)
return rc;
@@ -1743,7 +1775,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
if (!disk)
goto out;
- snprintf(disk->disk_name, sizeof(disk->disk_name), DRV_NAME "%d",
+ snprintf(disk->disk_name, sizeof(disk->disk_name), RBD_DRV_NAME "%d",
rbd_dev->id);
disk->major = rbd_dev->major;
disk->first_minor = 0;
@@ -1756,11 +1788,15 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
if (!q)
goto out_disk;
+ /* We use the default size, but let's be explicit about it. */
+ blk_queue_physical_block_size(q, SECTOR_SIZE);
+
/* set io sizes to object size */
- blk_queue_max_hw_sectors(q, rbd_obj_bytes(&rbd_dev->header) / 512ULL);
- blk_queue_max_segment_size(q, rbd_obj_bytes(&rbd_dev->header));
- blk_queue_io_min(q, rbd_obj_bytes(&rbd_dev->header));
- blk_queue_io_opt(q, rbd_obj_bytes(&rbd_dev->header));
+ segment_size = rbd_obj_bytes(&rbd_dev->header);
+ blk_queue_max_hw_sectors(q, segment_size / SECTOR_SIZE);
+ blk_queue_max_segment_size(q, segment_size);
+ blk_queue_io_min(q, segment_size);
+ blk_queue_io_opt(q, segment_size);
blk_queue_merge_bvec(q, rbd_merge_bvec);
disk->queue = q;
@@ -1771,7 +1807,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
rbd_dev->q = q;
/* finally, announce the disk to the world */
- set_capacity(disk, total_size / 512ULL);
+ set_capacity(disk, total_size / SECTOR_SIZE);
add_disk(disk);
pr_info("%s: added with size 0x%llx\n",
@@ -1788,10 +1824,15 @@ out:
sysfs
*/
+static struct rbd_device *dev_to_rbd_dev(struct device *dev)
+{
+ return container_of(dev, struct rbd_device, dev);
+}
+
static ssize_t rbd_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct rbd_device *rbd_dev = dev_to_rbd(dev);
+ struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
return sprintf(buf, "%llu\n", (unsigned long long)rbd_dev->header.image_size);
}
@@ -1799,7 +1840,7 @@ static ssize_t rbd_size_show(struct device *dev,
static ssize_t rbd_major_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct rbd_device *rbd_dev = dev_to_rbd(dev);
+ struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
return sprintf(buf, "%d\n", rbd_dev->major);
}
@@ -1807,15 +1848,16 @@ static ssize_t rbd_major_show(struct device *dev,
static ssize_t rbd_client_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct rbd_device *rbd_dev = dev_to_rbd(dev);
+ struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
- return sprintf(buf, "client%lld\n", ceph_client_id(rbd_dev->client));
+ return sprintf(buf, "client%lld\n",
+ ceph_client_id(rbd_dev->rbd_client->client));
}
static ssize_t rbd_pool_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct rbd_device *rbd_dev = dev_to_rbd(dev);
+ struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
return sprintf(buf, "%s\n", rbd_dev->pool_name);
}
@@ -1823,7 +1865,7 @@ static ssize_t rbd_pool_show(struct device *dev,
static ssize_t rbd_name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct rbd_device *rbd_dev = dev_to_rbd(dev);
+ struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
return sprintf(buf, "%s\n", rbd_dev->obj);
}
@@ -1832,7 +1874,7 @@ static ssize_t rbd_snap_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct rbd_device *rbd_dev = dev_to_rbd(dev);
+ struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
return sprintf(buf, "%s\n", rbd_dev->snap_name);
}
@@ -1842,7 +1884,7 @@ static ssize_t rbd_image_refresh(struct device *dev,
const char *buf,
size_t size)
{
- struct rbd_device *rbd_dev = dev_to_rbd(dev);
+ struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
int rc;
int ret = size;
@@ -1907,7 +1949,7 @@ static ssize_t rbd_snap_size_show(struct device *dev,
{
struct rbd_snap *snap = container_of(dev, struct rbd_snap, dev);
- return sprintf(buf, "%lld\n", (long long)snap->size);
+ return sprintf(buf, "%zd\n", snap->size);
}
static ssize_t rbd_snap_id_show(struct device *dev,
@@ -1916,7 +1958,7 @@ static ssize_t rbd_snap_id_show(struct device *dev,
{
struct rbd_snap *snap = container_of(dev, struct rbd_snap, dev);
- return sprintf(buf, "%lld\n", (long long)snap->id);
+ return sprintf(buf, "%llu\n", (unsigned long long) snap->id);
}
static DEVICE_ATTR(snap_size, S_IRUGO, rbd_snap_size_show, NULL);
@@ -2088,19 +2130,9 @@ static int __rbd_init_snaps_header(struct rbd_device *rbd_dev)
return 0;
}
-
-static void rbd_root_dev_release(struct device *dev)
-{
-}
-
-static struct device rbd_root_dev = {
- .init_name = "rbd",
- .release = rbd_root_dev_release,
-};
-
static int rbd_bus_add_dev(struct rbd_device *rbd_dev)
{
- int ret = -ENOMEM;
+ int ret;
struct device *dev;
struct rbd_snap *snap;
@@ -2114,7 +2146,7 @@ static int rbd_bus_add_dev(struct rbd_device *rbd_dev)
dev_set_name(dev, "%d", rbd_dev->id);
ret = device_register(dev);
if (ret < 0)
- goto done_free;
+ goto out;
list_for_each_entry(snap, &rbd_dev->snaps, node) {
ret = rbd_register_snap_dev(rbd_dev, snap,
@@ -2122,10 +2154,7 @@ static int rbd_bus_add_dev(struct rbd_device *rbd_dev)
if (ret < 0)
break;
}
-
- mutex_unlock(&ctl_mutex);
- return 0;
-done_free:
+out:
mutex_unlock(&ctl_mutex);
return ret;
}
@@ -2154,104 +2183,250 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev)
return ret;
}
+static atomic64_t rbd_id_max = ATOMIC64_INIT(0);
+
+/*
+ * Get a unique rbd identifier for the given new rbd_dev, and add
+ * the rbd_dev to the global list. The minimum rbd id is 1.
+ */
+static void rbd_id_get(struct rbd_device *rbd_dev)
+{
+ rbd_dev->id = atomic64_inc_return(&rbd_id_max);
+
+ spin_lock(&rbd_dev_list_lock);
+ list_add_tail(&rbd_dev->node, &rbd_dev_list);
+ spin_unlock(&rbd_dev_list_lock);
+}
+
+/*
+ * Remove an rbd_dev from the global list, and record that its
+ * identifier is no longer in use.
+ */
+static void rbd_id_put(struct rbd_device *rbd_dev)
+{
+ struct list_head *tmp;
+ int rbd_id = rbd_dev->id;
+ int max_id;
+
+ BUG_ON(rbd_id < 1);
+
+ spin_lock(&rbd_dev_list_lock);
+ list_del_init(&rbd_dev->node);
+
+ /*
+ * If the id being "put" is not the current maximum, there
+ * is nothing special we need to do.
+ */
+ if (rbd_id != atomic64_read(&rbd_id_max)) {
+ spin_unlock(&rbd_dev_list_lock);
+ return;
+ }
+
+ /*
+ * We need to update the current maximum id. Search the
+ * list to find out what it is. We're more likely to find
+ * the maximum at the end, so search the list backward.
+ */
+ max_id = 0;
+ list_for_each_prev(tmp, &rbd_dev_list) {
+ struct rbd_device *rbd_dev;
+
+ rbd_dev = list_entry(tmp, struct rbd_device, node);
+ if (rbd_id > max_id)
+ max_id = rbd_id;
+ }
+ spin_unlock(&rbd_dev_list_lock);
+
+ /*
+ * The max id could have been updated by rbd_id_get(), in
+ * which case it now accurately reflects the new maximum.
+ * Be careful not to overwrite the maximum value in that
+ * case.
+ */
+ atomic64_cmpxchg(&rbd_id_max, rbd_id, max_id);
+}
+
+/*
+ * Skips over white space at *buf, and updates *buf to point to the
+ * first found non-space character (if any). Returns the length of
+ * the token (string of non-white space characters) found. Note
+ * that *buf must be terminated with '\0'.
+ */
+static inline size_t next_token(const char **buf)
+{
+ /*
+ * These are the characters that produce nonzero for
+ * isspace() in the "C" and "POSIX" locales.
+ */
+ const char *spaces = " \f\n\r\t\v";
+
+ *buf += strspn(*buf, spaces); /* Find start of token */
+
+ return strcspn(*buf, spaces); /* Return token length */
+}
+
+/*
+ * Finds the next token in *buf, and if the provided token buffer is
+ * big enough, copies the found token into it. The result, if
+ * copied, is guaranteed to be terminated with '\0'. Note that *buf
+ * must be terminated with '\0' on entry.
+ *
+ * Returns the length of the token found (not including the '\0').
+ * Return value will be 0 if no token is found, and it will be >=
+ * token_size if the token would not fit.
+ *
+ * The *buf pointer will be updated to point beyond the end of the
+ * found token. Note that this occurs even if the token buffer is
+ * too small to hold it.
+ */
+static inline size_t copy_token(const char **buf,
+ char *token,
+ size_t token_size)
+{
+ size_t len;
+
+ len = next_token(buf);
+ if (len < token_size) {
+ memcpy(token, *buf, len);
+ *(token + len) = '\0';
+ }
+ *buf += len;
+
+ return len;
+}
+
+/*
+ * This fills in the pool_name, obj, obj_len, snap_name, obj_len,
+ * rbd_dev, rbd_md_name, and name fields of the given rbd_dev, based
+ * on the list of monitor addresses and other options provided via
+ * /sys/bus/rbd/add.
+ */
+static int rbd_add_parse_args(struct rbd_device *rbd_dev,
+ const char *buf,
+ const char **mon_addrs,
+ size_t *mon_addrs_size,
+ char *options,
+ size_t options_size)
+{
+ size_t len;
+
+ /* The first four tokens are required */
+
+ len = next_token(&buf);
+ if (!len)
+ return -EINVAL;
+ *mon_addrs_size = len + 1;
+ *mon_addrs = buf;
+
+ buf += len;
+
+ len = copy_token(&buf, options, options_size);
+ if (!len || len >= options_size)
+ return -EINVAL;
+
+ len = copy_token(&buf, rbd_dev->pool_name, sizeof (rbd_dev->pool_name));
+ if (!len || len >= sizeof (rbd_dev->pool_name))
+ return -EINVAL;
+
+ len = copy_token(&buf, rbd_dev->obj, sizeof (rbd_dev->obj));
+ if (!len || len >= sizeof (rbd_dev->obj))
+ return -EINVAL;
+
+ /* We have the object length in hand, save it. */
+
+ rbd_dev->obj_len = len;
+
+ BUILD_BUG_ON(RBD_MAX_MD_NAME_LEN
+ < RBD_MAX_OBJ_NAME_LEN + sizeof (RBD_SUFFIX));
+ sprintf(rbd_dev->obj_md_name, "%s%s", rbd_dev->obj, RBD_SUFFIX);
+
+ /*
+ * The snapshot name is optional, but it's an error if it's
+ * too long. If no snapshot is supplied, fill in the default.
+ */
+ len = copy_token(&buf, rbd_dev->snap_name, sizeof (rbd_dev->snap_name));
+ if (!len)
+ memcpy(rbd_dev->snap_name, RBD_SNAP_HEAD_NAME,
+ sizeof (RBD_SNAP_HEAD_NAME));
+ else if (len >= sizeof (rbd_dev->snap_name))
+ return -EINVAL;
+
+ return 0;
+}
+
static ssize_t rbd_add(struct bus_type *bus,
const char *buf,
size_t count)
{
- struct ceph_osd_client *osdc;
struct rbd_device *rbd_dev;
- ssize_t rc = -ENOMEM;
- int irc, new_id = 0;
- struct list_head *tmp;
- char *mon_dev_name;
- char *options;
+ const char *mon_addrs = NULL;
+ size_t mon_addrs_size = 0;
+ char *options = NULL;
+ struct ceph_osd_client *osdc;
+ int rc = -ENOMEM;
if (!try_module_get(THIS_MODULE))
return -ENODEV;
- mon_dev_name = kmalloc(RBD_MAX_OPT_LEN, GFP_KERNEL);
- if (!mon_dev_name)
- goto err_out_mod;
-
- options = kmalloc(RBD_MAX_OPT_LEN, GFP_KERNEL);
- if (!options)
- goto err_mon_dev;
-
- /* new rbd_device object */
rbd_dev = kzalloc(sizeof(*rbd_dev), GFP_KERNEL);
if (!rbd_dev)
- goto err_out_opt;
+ goto err_nomem;
+ options = kmalloc(count, GFP_KERNEL);
+ if (!options)
+ goto err_nomem;
/* static rbd_device initialization */
spin_lock_init(&rbd_dev->lock);
INIT_LIST_HEAD(&rbd_dev->node);
INIT_LIST_HEAD(&rbd_dev->snaps);
+ init_rwsem(&rbd_dev->header_rwsem);
- init_rwsem(&rbd_dev->header.snap_rwsem);
+ init_rwsem(&rbd_dev->header_rwsem);
/* generate unique id: find highest unique id, add one */
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-
- list_for_each(tmp, &rbd_dev_list) {
- struct rbd_device *rbd_dev;
+ rbd_id_get(rbd_dev);
- rbd_dev = list_entry(tmp, struct rbd_device, node);
- if (rbd_dev->id >= new_id)
- new_id = rbd_dev->id + 1;
- }
-
- rbd_dev->id = new_id;
-
- /* add to global list */
- list_add_tail(&rbd_dev->node, &rbd_dev_list);
+ /* Fill in the device name, now that we have its id. */
+ BUILD_BUG_ON(DEV_NAME_LEN
+ < sizeof (RBD_DRV_NAME) + MAX_INT_FORMAT_WIDTH);
+ sprintf(rbd_dev->name, "%s%d", RBD_DRV_NAME, rbd_dev->id);
/* parse add command */
- if (sscanf(buf, "%" __stringify(RBD_MAX_OPT_LEN) "s "
- "%" __stringify(RBD_MAX_OPT_LEN) "s "
- "%" __stringify(RBD_MAX_POOL_NAME_LEN) "s "
- "%" __stringify(RBD_MAX_OBJ_NAME_LEN) "s"
- "%" __stringify(RBD_MAX_SNAP_NAME_LEN) "s",
- mon_dev_name, options, rbd_dev->pool_name,
- rbd_dev->obj, rbd_dev->snap_name) < 4) {
- rc = -EINVAL;
- goto err_out_slot;
- }
-
- if (rbd_dev->snap_name[0] == 0)
- rbd_dev->snap_name[0] = '-';
-
- rbd_dev->obj_len = strlen(rbd_dev->obj);
- snprintf(rbd_dev->obj_md_name, sizeof(rbd_dev->obj_md_name), "%s%s",
- rbd_dev->obj, RBD_SUFFIX);
-
- /* initialize rest of new object */
- snprintf(rbd_dev->name, DEV_NAME_LEN, DRV_NAME "%d", rbd_dev->id);
- rc = rbd_get_client(rbd_dev, mon_dev_name, options);
- if (rc < 0)
- goto err_out_slot;
+ rc = rbd_add_parse_args(rbd_dev, buf, &mon_addrs, &mon_addrs_size,
+ options, count);
+ if (rc)
+ goto err_put_id;
- mutex_unlock(&ctl_mutex);
+ rbd_dev->rbd_client = rbd_get_client(mon_addrs, mon_addrs_size - 1,
+ options);
+ if (IS_ERR(rbd_dev->rbd_client)) {
+ rc = PTR_ERR(rbd_dev->rbd_client);
+ goto err_put_id;
+ }
/* pick the pool */
- osdc = &rbd_dev->client->osdc;
+ osdc = &rbd_dev->rbd_client->client->osdc;
rc = ceph_pg_poolid_by_name(osdc->osdmap, rbd_dev->pool_name);
if (rc < 0)
goto err_out_client;
rbd_dev->poolid = rc;
/* register our block device */
- irc = register_blkdev(0, rbd_dev->name);
- if (irc < 0) {
- rc = irc;
+ rc = register_blkdev(0, rbd_dev->name);
+ if (rc < 0)
goto err_out_client;
- }
- rbd_dev->major = irc;
+ rbd_dev->major = rc;
rc = rbd_bus_add_dev(rbd_dev);
if (rc)
goto err_out_blkdev;
- /* set up and announce blkdev mapping */
+ /*
+ * At this point cleanup in the event of an error is the job
+ * of the sysfs code (initiated by rbd_bus_del_dev()).
+ *
+ * Set up and announce blkdev mapping.
+ */
rc = rbd_init_disk(rbd_dev);
if (rc)
goto err_out_bus;
@@ -2263,35 +2438,26 @@ static ssize_t rbd_add(struct bus_type *bus,
return count;
err_out_bus:
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
- list_del_init(&rbd_dev->node);
- mutex_unlock(&ctl_mutex);
-
/* this will also clean up rest of rbd_dev stuff */
rbd_bus_del_dev(rbd_dev);
kfree(options);
- kfree(mon_dev_name);
return rc;
err_out_blkdev:
unregister_blkdev(rbd_dev->major, rbd_dev->name);
err_out_client:
rbd_put_client(rbd_dev);
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-err_out_slot:
- list_del_init(&rbd_dev->node);
- mutex_unlock(&ctl_mutex);
-
- kfree(rbd_dev);
-err_out_opt:
+err_put_id:
+ rbd_id_put(rbd_dev);
+err_nomem:
kfree(options);
-err_mon_dev:
- kfree(mon_dev_name);
-err_out_mod:
+ kfree(rbd_dev);
+
dout("Error adding device %s\n", buf);
module_put(THIS_MODULE);
- return rc;
+
+ return (ssize_t) rc;
}
static struct rbd_device *__rbd_get_dev(unsigned long id)
@@ -2299,22 +2465,28 @@ static struct rbd_device *__rbd_get_dev(unsigned long id)
struct list_head *tmp;
struct rbd_device *rbd_dev;
+ spin_lock(&rbd_dev_list_lock);
list_for_each(tmp, &rbd_dev_list) {
rbd_dev = list_entry(tmp, struct rbd_device, node);
- if (rbd_dev->id == id)
+ if (rbd_dev->id == id) {
+ spin_unlock(&rbd_dev_list_lock);
return rbd_dev;
+ }
}
+ spin_unlock(&rbd_dev_list_lock);
return NULL;
}
static void rbd_dev_release(struct device *dev)
{
- struct rbd_device *rbd_dev =
- container_of(dev, struct rbd_device, dev);
+ struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
- if (rbd_dev->watch_request)
- ceph_osdc_unregister_linger_request(&rbd_dev->client->osdc,
+ if (rbd_dev->watch_request) {
+ struct ceph_client *client = rbd_dev->rbd_client->client;
+
+ ceph_osdc_unregister_linger_request(&client->osdc,
rbd_dev->watch_request);
+ }
if (rbd_dev->watch_event)
rbd_req_sync_unwatch(rbd_dev, rbd_dev->obj_md_name);
@@ -2323,6 +2495,9 @@ static void rbd_dev_release(struct device *dev)
/* clean up and free blkdev */
rbd_free_disk(rbd_dev);
unregister_blkdev(rbd_dev->major, rbd_dev->name);
+
+ /* done with the id, and with the rbd_dev */
+ rbd_id_put(rbd_dev);
kfree(rbd_dev);
/* release module ref */
@@ -2355,8 +2530,6 @@ static ssize_t rbd_remove(struct bus_type *bus,
goto done;
}
- list_del_init(&rbd_dev->node);
-
__rbd_remove_all_snaps(rbd_dev);
rbd_bus_del_dev(rbd_dev);
@@ -2370,7 +2543,7 @@ static ssize_t rbd_snap_add(struct device *dev,
const char *buf,
size_t count)
{
- struct rbd_device *rbd_dev = dev_to_rbd(dev);
+ struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
int ret;
char *name = kmalloc(count + 1, GFP_KERNEL);
if (!name)
@@ -2406,12 +2579,6 @@ err_unlock:
return ret;
}
-static struct bus_attribute rbd_bus_attrs[] = {
- __ATTR(add, S_IWUSR, NULL, rbd_add),
- __ATTR(remove, S_IWUSR, NULL, rbd_remove),
- __ATTR_NULL
-};
-
/*
* create control files in sysfs
* /sys/bus/rbd/...
@@ -2420,21 +2587,21 @@ static int rbd_sysfs_init(void)
{
int ret;
- rbd_bus_type.bus_attrs = rbd_bus_attrs;
-
- ret = bus_register(&rbd_bus_type);
- if (ret < 0)
+ ret = device_register(&rbd_root_dev);
+ if (ret < 0)
return ret;
- ret = device_register(&rbd_root_dev);
+ ret = bus_register(&rbd_bus_type);
+ if (ret < 0)
+ device_unregister(&rbd_root_dev);
return ret;
}
static void rbd_sysfs_cleanup(void)
{
- device_unregister(&rbd_root_dev);
bus_unregister(&rbd_bus_type);
+ device_unregister(&rbd_root_dev);
}
int __init rbd_init(void)
@@ -2444,8 +2611,7 @@ int __init rbd_init(void)
rc = rbd_sysfs_init();
if (rc)
return rc;
- spin_lock_init(&node_lock);
- pr_info("loaded " DRV_NAME_LONG "\n");
+ pr_info("loaded " RBD_DRV_NAME_LONG "\n");
return 0;
}
diff --git a/drivers/block/rbd_types.h b/drivers/block/rbd_types.h
index fc6c678..9507086 100644
--- a/drivers/block/rbd_types.h
+++ b/drivers/block/rbd_types.h
@@ -41,10 +41,6 @@
#define RBD_HEADER_SIGNATURE "RBD"
#define RBD_HEADER_VERSION "001.005"
-struct rbd_info {
- __le64 max_id;
-} __attribute__ ((packed));
-
struct rbd_image_snap_ondisk {
__le64 id;
__le64 image_size;
diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c
index 48e8fee..9dcf76a 100644
--- a/drivers/block/sunvdc.c
+++ b/drivers/block/sunvdc.c
@@ -839,10 +839,7 @@ static struct vio_driver vdc_port_driver = {
.id_table = vdc_port_match,
.probe = vdc_port_probe,
.remove = vdc_port_remove,
- .driver = {
- .name = "vdc_port",
- .owner = THIS_MODULE,
- }
+ .name = "vdc_port",
};
static int __init vdc_init(void)
diff --git a/drivers/block/xd.c b/drivers/block/xd.c
index 51a9727..ff54052 100644
--- a/drivers/block/xd.c
+++ b/drivers/block/xd.c
@@ -52,7 +52,6 @@
#include <linux/io.h>
#include <linux/gfp.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/dma.h>
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 9c09d6f..308c859 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -39,7 +39,6 @@
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <linux/device.h>
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index 194224d..c4fc2f3 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -38,7 +38,6 @@
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <pcmcia/cistpl.h>
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index 049c059..6e8d961 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -38,7 +38,6 @@
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <pcmcia/cistpl.h>
diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c
index f4837a8..57501ca 100644
--- a/drivers/char/apm-emulation.c
+++ b/drivers/char/apm-emulation.c
@@ -31,7 +31,6 @@
#include <linux/kthread.h>
#include <linux/delay.h>
-#include <asm/system.h>
/*
* The apm_bios device is one of the misc char devices.
diff --git a/drivers/char/ds1302.c b/drivers/char/ds1302.c
index ed8303f9..7d34b20 100644
--- a/drivers/char/ds1302.c
+++ b/drivers/char/ds1302.c
@@ -24,7 +24,6 @@
#include <linux/uaccess.h>
#include <linux/io.h>
-#include <asm/system.h>
#include <asm/rtc.h>
#if defined(CONFIG_M32R)
#include <asm/m32r.h>
diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c
index 53c524e..a082d00 100644
--- a/drivers/char/efirtc.c
+++ b/drivers/char/efirtc.c
@@ -37,7 +37,6 @@
#include <linux/efi.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#define EFI_RTC_VERSION "0.4"
diff --git a/drivers/char/genrtc.c b/drivers/char/genrtc.c
index f773a9d..21cb980 100644
--- a/drivers/char/genrtc.c
+++ b/drivers/char/genrtc.c
@@ -56,7 +56,6 @@
#include <linux/workqueue.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#include <asm/rtc.h>
/*
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 0833896..3845ab4 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -36,7 +36,6 @@
#include <linux/io.h>
#include <asm/current.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <asm/div64.h>
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index 2aa3977..9eb360f 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -34,7 +34,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
-#include <asm/system.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c
index cf82fed..e53fc24 100644
--- a/drivers/char/ipmi/ipmi_kcs_sm.c
+++ b/drivers/char/ipmi/ipmi_kcs_sm.c
@@ -118,8 +118,8 @@ enum kcs_states {
#define MAX_KCS_WRITE_SIZE IPMI_MAX_MSG_LENGTH
/* Timeouts in microseconds. */
-#define IBF_RETRY_TIMEOUT 1000000
-#define OBF_RETRY_TIMEOUT 1000000
+#define IBF_RETRY_TIMEOUT 5000000
+#define OBF_RETRY_TIMEOUT 5000000
#define MAX_ERROR_RETRIES 10
#define ERROR0_OBF_WAIT_JIFFIES (2*HZ)
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 58c0e63..2c29942 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -33,7 +33,6 @@
#include <linux/module.h>
#include <linux/errno.h>
-#include <asm/system.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
@@ -46,6 +45,7 @@
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/rcupdate.h>
+#include <linux/interrupt.h>
#define PFX "IPMI message handler: "
@@ -53,6 +53,8 @@
static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
static int ipmi_init_msghandler(void);
+static void smi_recv_tasklet(unsigned long);
+static void handle_new_recv_msgs(ipmi_smi_t intf);
static int initialized;
@@ -355,12 +357,15 @@ struct ipmi_smi {
int curr_seq;
/*
- * Messages that were delayed for some reason (out of memory,
- * for instance), will go in here to be processed later in a
- * periodic timer interrupt.
+ * Messages queued for delivery. If delivery fails (out of memory
+ * for instance), They will stay in here to be processed later in a
+ * periodic timer interrupt. The tasklet is for handling received
+ * messages directly from the handler.
*/
spinlock_t waiting_msgs_lock;
struct list_head waiting_msgs;
+ atomic_t watchdog_pretimeouts_to_deliver;
+ struct tasklet_struct recv_tasklet;
/*
* The list of command receivers that are registered for commands
@@ -493,6 +498,8 @@ static void clean_up_interface_data(ipmi_smi_t intf)
struct cmd_rcvr *rcvr, *rcvr2;
struct list_head list;
+ tasklet_kill(&intf->recv_tasklet);
+
free_smi_msg_list(&intf->waiting_msgs);
free_recv_msg_list(&intf->waiting_events);
@@ -2786,12 +2793,17 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
return;
}
-void ipmi_poll_interface(ipmi_user_t user)
+static void ipmi_poll(ipmi_smi_t intf)
{
- ipmi_smi_t intf = user->intf;
-
if (intf->handlers->poll)
intf->handlers->poll(intf->send_info);
+ /* In case something came in */
+ handle_new_recv_msgs(intf);
+}
+
+void ipmi_poll_interface(ipmi_user_t user)
+{
+ ipmi_poll(user->intf);
}
EXPORT_SYMBOL(ipmi_poll_interface);
@@ -2860,6 +2872,10 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
#endif
spin_lock_init(&intf->waiting_msgs_lock);
INIT_LIST_HEAD(&intf->waiting_msgs);
+ tasklet_init(&intf->recv_tasklet,
+ smi_recv_tasklet,
+ (unsigned long) intf);
+ atomic_set(&intf->watchdog_pretimeouts_to_deliver, 0);
spin_lock_init(&intf->events_lock);
INIT_LIST_HEAD(&intf->waiting_events);
intf->waiting_events_count = 0;
@@ -3622,11 +3638,11 @@ static int handle_bmc_rsp(ipmi_smi_t intf,
}
/*
- * Handle a new message. Return 1 if the message should be requeued,
+ * Handle a received message. Return 1 if the message should be requeued,
* 0 if the message should be freed, or -1 if the message should not
* be freed or requeued.
*/
-static int handle_new_recv_msg(ipmi_smi_t intf,
+static int handle_one_recv_msg(ipmi_smi_t intf,
struct ipmi_smi_msg *msg)
{
int requeue;
@@ -3784,12 +3800,72 @@ static int handle_new_recv_msg(ipmi_smi_t intf,
return requeue;
}
+/*
+ * If there are messages in the queue or pretimeouts, handle them.
+ */
+static void handle_new_recv_msgs(ipmi_smi_t intf)
+{
+ struct ipmi_smi_msg *smi_msg;
+ unsigned long flags = 0;
+ int rv;
+ int run_to_completion = intf->run_to_completion;
+
+ /* See if any waiting messages need to be processed. */
+ if (!run_to_completion)
+ spin_lock_irqsave(&intf->waiting_msgs_lock, flags);
+ while (!list_empty(&intf->waiting_msgs)) {
+ smi_msg = list_entry(intf->waiting_msgs.next,
+ struct ipmi_smi_msg, link);
+ list_del(&smi_msg->link);
+ if (!run_to_completion)
+ spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags);
+ rv = handle_one_recv_msg(intf, smi_msg);
+ if (!run_to_completion)
+ spin_lock_irqsave(&intf->waiting_msgs_lock, flags);
+ if (rv == 0) {
+ /* Message handled */
+ ipmi_free_smi_msg(smi_msg);
+ } else if (rv < 0) {
+ /* Fatal error on the message, del but don't free. */
+ } else {
+ /*
+ * To preserve message order, quit if we
+ * can't handle a message.
+ */
+ list_add(&smi_msg->link, &intf->waiting_msgs);
+ break;
+ }
+ }
+ if (!run_to_completion)
+ spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags);
+
+ /*
+ * If the pretimout count is non-zero, decrement one from it and
+ * deliver pretimeouts to all the users.
+ */
+ if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) {
+ ipmi_user_t user;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(user, &intf->users, link) {
+ if (user->handler->ipmi_watchdog_pretimeout)
+ user->handler->ipmi_watchdog_pretimeout(
+ user->handler_data);
+ }
+ rcu_read_unlock();
+ }
+}
+
+static void smi_recv_tasklet(unsigned long val)
+{
+ handle_new_recv_msgs((ipmi_smi_t) val);
+}
+
/* Handle a new message from the lower layer. */
void ipmi_smi_msg_received(ipmi_smi_t intf,
struct ipmi_smi_msg *msg)
{
unsigned long flags = 0; /* keep us warning-free. */
- int rv;
int run_to_completion;
@@ -3843,31 +3919,11 @@ void ipmi_smi_msg_received(ipmi_smi_t intf,
run_to_completion = intf->run_to_completion;
if (!run_to_completion)
spin_lock_irqsave(&intf->waiting_msgs_lock, flags);
- if (!list_empty(&intf->waiting_msgs)) {
- list_add_tail(&msg->link, &intf->waiting_msgs);
- if (!run_to_completion)
- spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags);
- goto out;
- }
+ list_add_tail(&msg->link, &intf->waiting_msgs);
if (!run_to_completion)
spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags);
- rv = handle_new_recv_msg(intf, msg);
- if (rv > 0) {
- /*
- * Could not handle the message now, just add it to a
- * list to handle later.
- */
- run_to_completion = intf->run_to_completion;
- if (!run_to_completion)
- spin_lock_irqsave(&intf->waiting_msgs_lock, flags);
- list_add_tail(&msg->link, &intf->waiting_msgs);
- if (!run_to_completion)
- spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags);
- } else if (rv == 0) {
- ipmi_free_smi_msg(msg);
- }
-
+ tasklet_schedule(&intf->recv_tasklet);
out:
return;
}
@@ -3875,16 +3931,8 @@ EXPORT_SYMBOL(ipmi_smi_msg_received);
void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf)
{
- ipmi_user_t user;
-
- rcu_read_lock();
- list_for_each_entry_rcu(user, &intf->users, link) {
- if (!user->handler->ipmi_watchdog_pretimeout)
- continue;
-
- user->handler->ipmi_watchdog_pretimeout(user->handler_data);
- }
- rcu_read_unlock();
+ atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1);
+ tasklet_schedule(&intf->recv_tasklet);
}
EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout);
@@ -3998,28 +4046,12 @@ static void ipmi_timeout_handler(long timeout_period)
ipmi_smi_t intf;
struct list_head timeouts;
struct ipmi_recv_msg *msg, *msg2;
- struct ipmi_smi_msg *smi_msg, *smi_msg2;
unsigned long flags;
int i;
rcu_read_lock();
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
- /* See if any waiting messages need to be processed. */
- spin_lock_irqsave(&intf->waiting_msgs_lock, flags);
- list_for_each_entry_safe(smi_msg, smi_msg2,
- &intf->waiting_msgs, link) {
- if (!handle_new_recv_msg(intf, smi_msg)) {
- list_del(&smi_msg->link);
- ipmi_free_smi_msg(smi_msg);
- } else {
- /*
- * To preserve message order, quit if we
- * can't handle a message.
- */
- break;
- }
- }
- spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags);
+ tasklet_schedule(&intf->recv_tasklet);
/*
* Go through the seq table and find any messages that
@@ -4173,12 +4205,48 @@ EXPORT_SYMBOL(ipmi_free_recv_msg);
#ifdef CONFIG_IPMI_PANIC_EVENT
+static atomic_t panic_done_count = ATOMIC_INIT(0);
+
static void dummy_smi_done_handler(struct ipmi_smi_msg *msg)
{
+ atomic_dec(&panic_done_count);
}
static void dummy_recv_done_handler(struct ipmi_recv_msg *msg)
{
+ atomic_dec(&panic_done_count);
+}
+
+/*
+ * Inside a panic, send a message and wait for a response.
+ */
+static void ipmi_panic_request_and_wait(ipmi_smi_t intf,
+ struct ipmi_addr *addr,
+ struct kernel_ipmi_msg *msg)
+{
+ struct ipmi_smi_msg smi_msg;
+ struct ipmi_recv_msg recv_msg;
+ int rv;
+
+ smi_msg.done = dummy_smi_done_handler;
+ recv_msg.done = dummy_recv_done_handler;
+ atomic_add(2, &panic_done_count);
+ rv = i_ipmi_request(NULL,
+ intf,
+ addr,
+ 0,
+ msg,
+ intf,
+ &smi_msg,
+ &recv_msg,
+ 0,
+ intf->channels[0].address,
+ intf->channels[0].lun,
+ 0, 1); /* Don't retry, and don't wait. */
+ if (rv)
+ atomic_sub(2, &panic_done_count);
+ while (atomic_read(&panic_done_count) != 0)
+ ipmi_poll(intf);
}
#ifdef CONFIG_IPMI_PANIC_STRING
@@ -4217,8 +4285,6 @@ static void send_panic_events(char *str)
unsigned char data[16];
struct ipmi_system_interface_addr *si;
struct ipmi_addr addr;
- struct ipmi_smi_msg smi_msg;
- struct ipmi_recv_msg recv_msg;
si = (struct ipmi_system_interface_addr *) &addr;
si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
@@ -4246,9 +4312,6 @@ static void send_panic_events(char *str)
data[7] = str[2];
}
- smi_msg.done = dummy_smi_done_handler;
- recv_msg.done = dummy_recv_done_handler;
-
/* For every registered interface, send the event. */
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
if (!intf->handlers)
@@ -4258,18 +4321,7 @@ static void send_panic_events(char *str)
intf->run_to_completion = 1;
/* Send the event announcing the panic. */
intf->handlers->set_run_to_completion(intf->send_info, 1);
- i_ipmi_request(NULL,
- intf,
- &addr,
- 0,
- &msg,
- intf,
- &smi_msg,
- &recv_msg,
- 0,
- intf->channels[0].address,
- intf->channels[0].lun,
- 0, 1); /* Don't retry, and don't wait. */
+ ipmi_panic_request_and_wait(intf, &addr, &msg);
}
#ifdef CONFIG_IPMI_PANIC_STRING
@@ -4317,18 +4369,7 @@ static void send_panic_events(char *str)
msg.data = NULL;
msg.data_len = 0;
intf->null_user_handler = device_id_fetcher;
- i_ipmi_request(NULL,
- intf,
- &addr,
- 0,
- &msg,
- intf,
- &smi_msg,
- &recv_msg,
- 0,
- intf->channels[0].address,
- intf->channels[0].lun,
- 0, 1); /* Don't retry, and don't wait. */
+ ipmi_panic_request_and_wait(intf, &addr, &msg);
if (intf->local_event_generator) {
/* Request the event receiver from the local MC. */
@@ -4337,18 +4378,7 @@ static void send_panic_events(char *str)
msg.data = NULL;
msg.data_len = 0;
intf->null_user_handler = event_receiver_fetcher;
- i_ipmi_request(NULL,
- intf,
- &addr,
- 0,
- &msg,
- intf,
- &smi_msg,
- &recv_msg,
- 0,
- intf->channels[0].address,
- intf->channels[0].lun,
- 0, 1); /* no retry, and no wait. */
+ ipmi_panic_request_and_wait(intf, &addr, &msg);
}
intf->null_user_handler = NULL;
@@ -4405,18 +4435,7 @@ static void send_panic_events(char *str)
strncpy(data+5, p, 11);
p += size;
- i_ipmi_request(NULL,
- intf,
- &addr,
- 0,
- &msg,
- intf,
- &smi_msg,
- &recv_msg,
- 0,
- intf->channels[0].address,
- intf->channels[0].lun,
- 0, 1); /* no retry, and no wait. */
+ ipmi_panic_request_and_wait(intf, &addr, &msg);
}
}
#endif /* CONFIG_IPMI_PANIC_STRING */
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 50fcf9c..1e638ff 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -41,7 +41,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <asm/system.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/timer.h>
@@ -171,7 +170,6 @@ struct smi_info {
struct si_sm_handlers *handlers;
enum si_type si_type;
spinlock_t si_lock;
- spinlock_t msg_lock;
struct list_head xmit_msgs;
struct list_head hp_xmit_msgs;
struct ipmi_smi_msg *curr_msg;
@@ -320,16 +318,8 @@ static int register_xaction_notifier(struct notifier_block *nb)
static void deliver_recv_msg(struct smi_info *smi_info,
struct ipmi_smi_msg *msg)
{
- /* Deliver the message to the upper layer with the lock
- released. */
-
- if (smi_info->run_to_completion) {
- ipmi_smi_msg_received(smi_info->intf, msg);
- } else {
- spin_unlock(&(smi_info->si_lock));
- ipmi_smi_msg_received(smi_info->intf, msg);
- spin_lock(&(smi_info->si_lock));
- }
+ /* Deliver the message to the upper layer. */
+ ipmi_smi_msg_received(smi_info->intf, msg);
}
static void return_hosed_msg(struct smi_info *smi_info, int cCode)
@@ -358,13 +348,6 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info)
struct timeval t;
#endif
- /*
- * No need to save flags, we aleady have interrupts off and we
- * already hold the SMI lock.
- */
- if (!smi_info->run_to_completion)
- spin_lock(&(smi_info->msg_lock));
-
/* Pick the high priority queue first. */
if (!list_empty(&(smi_info->hp_xmit_msgs))) {
entry = smi_info->hp_xmit_msgs.next;
@@ -402,9 +385,6 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info)
rv = SI_SM_CALL_WITHOUT_DELAY;
}
out:
- if (!smi_info->run_to_completion)
- spin_unlock(&(smi_info->msg_lock));
-
return rv;
}
@@ -481,9 +461,7 @@ static void handle_flags(struct smi_info *smi_info)
start_clear_flags(smi_info);
smi_info->msg_flags &= ~WDT_PRE_TIMEOUT_INT;
- spin_unlock(&(smi_info->si_lock));
ipmi_smi_watchdog_pretimeout(smi_info->intf);
- spin_lock(&(smi_info->si_lock));
} else if (smi_info->msg_flags & RECEIVE_MSG_AVAIL) {
/* Messages available. */
smi_info->curr_msg = ipmi_alloc_smi_msg();
@@ -889,19 +867,6 @@ static void sender(void *send_info,
printk("**Enqueue: %d.%9.9d\n", t.tv_sec, t.tv_usec);
#endif
- /*
- * last_timeout_jiffies is updated here to avoid
- * smi_timeout() handler passing very large time_diff
- * value to smi_event_handler() that causes
- * the send command to abort.
- */
- smi_info->last_timeout_jiffies = jiffies;
-
- mod_timer(&smi_info->si_timer, jiffies + SI_TIMEOUT_JIFFIES);
-
- if (smi_info->thread)
- wake_up_process(smi_info->thread);
-
if (smi_info->run_to_completion) {
/*
* If we are running to completion, then throw it in
@@ -924,16 +889,29 @@ static void sender(void *send_info,
return;
}
- spin_lock_irqsave(&smi_info->msg_lock, flags);
+ spin_lock_irqsave(&smi_info->si_lock, flags);
if (priority > 0)
list_add_tail(&msg->link, &smi_info->hp_xmit_msgs);
else
list_add_tail(&msg->link, &smi_info->xmit_msgs);
- spin_unlock_irqrestore(&smi_info->msg_lock, flags);
- spin_lock_irqsave(&smi_info->si_lock, flags);
- if (smi_info->si_state == SI_NORMAL && smi_info->curr_msg == NULL)
+ if (smi_info->si_state == SI_NORMAL && smi_info->curr_msg == NULL) {
+ /*
+ * last_timeout_jiffies is updated here to avoid
+ * smi_timeout() handler passing very large time_diff
+ * value to smi_event_handler() that causes
+ * the send command to abort.
+ */
+ smi_info->last_timeout_jiffies = jiffies;
+
+ mod_timer(&smi_info->si_timer, jiffies + SI_TIMEOUT_JIFFIES);
+
+ if (smi_info->thread)
+ wake_up_process(smi_info->thread);
+
start_next_msg(smi_info);
+ smi_event_handler(smi_info, 0);
+ }
spin_unlock_irqrestore(&smi_info->si_lock, flags);
}
@@ -1034,16 +1012,19 @@ static int ipmi_thread(void *data)
static void poll(void *send_info)
{
struct smi_info *smi_info = send_info;
- unsigned long flags;
+ unsigned long flags = 0;
+ int run_to_completion = smi_info->run_to_completion;
/*
* Make sure there is some delay in the poll loop so we can
* drive time forward and timeout things.
*/
udelay(10);
- spin_lock_irqsave(&smi_info->si_lock, flags);
+ if (!run_to_completion)
+ spin_lock_irqsave(&smi_info->si_lock, flags);
smi_event_handler(smi_info, 10);
- spin_unlock_irqrestore(&smi_info->si_lock, flags);
+ if (!run_to_completion)
+ spin_unlock_irqrestore(&smi_info->si_lock, flags);
}
static void request_events(void *send_info)
@@ -1680,10 +1661,8 @@ static struct smi_info *smi_info_alloc(void)
{
struct smi_info *info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (info) {
+ if (info)
spin_lock_init(&info->si_lock);
- spin_lock_init(&info->msg_lock);
- }
return info;
}
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 34767a6..7ed356e 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -153,7 +153,7 @@
#endif
static DEFINE_MUTEX(ipmi_watchdog_mutex);
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static ipmi_user_t watchdog_user;
static int watchdog_ifnum;
@@ -320,7 +320,7 @@ module_param(start_now, int, 0444);
MODULE_PARM_DESC(start_now, "Set to 1 to start the watchdog as"
"soon as the driver is loaded.");
-module_param(nowayout, int, 0644);
+module_param(nowayout, bool, 0644);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=CONFIG_WATCHDOG_NOWAYOUT)");
@@ -520,6 +520,7 @@ static void panic_halt_ipmi_heartbeat(void)
msg.cmd = IPMI_WDOG_RESET_TIMER;
msg.data = NULL;
msg.data_len = 0;
+ atomic_add(2, &panic_done_count);
rv = ipmi_request_supply_msgs(watchdog_user,
(struct ipmi_addr *) &addr,
0,
@@ -528,8 +529,8 @@ static void panic_halt_ipmi_heartbeat(void)
&panic_halt_heartbeat_smi_msg,
&panic_halt_heartbeat_recv_msg,
1);
- if (!rv)
- atomic_add(2, &panic_done_count);
+ if (rv)
+ atomic_sub(2, &panic_done_count);
}
static struct ipmi_smi_msg panic_halt_smi_msg = {
@@ -553,16 +554,18 @@ static void panic_halt_ipmi_set_timeout(void)
/* Wait for the messages to be free. */
while (atomic_read(&panic_done_count) != 0)
ipmi_poll_interface(watchdog_user);
+ atomic_add(2, &panic_done_count);
rv = i_ipmi_set_timeout(&panic_halt_smi_msg,
&panic_halt_recv_msg,
&send_heartbeat_now);
- if (!rv) {
- atomic_add(2, &panic_done_count);
- if (send_heartbeat_now)
- panic_halt_ipmi_heartbeat();
- } else
+ if (rv) {
+ atomic_sub(2, &panic_done_count);
printk(KERN_WARNING PFX
"Unable to extend the watchdog timeout.");
+ } else {
+ if (send_heartbeat_now)
+ panic_halt_ipmi_heartbeat();
+ }
while (atomic_read(&panic_done_count) != 0)
ipmi_poll_interface(watchdog_user);
}
@@ -1164,7 +1167,7 @@ static int wdog_reboot_handler(struct notifier_block *this,
if (code == SYS_POWER_OFF || code == SYS_HALT) {
/* Disable the WDT if we are shutting down. */
ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
- panic_halt_ipmi_set_timeout();
+ ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB);
} else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) {
/* Set a long timer to let the reboot happens, but
reboot if it hangs, but only if the watchdog
@@ -1172,7 +1175,7 @@ static int wdog_reboot_handler(struct notifier_block *this,
timeout = 120;
pretimeout = 0;
ipmi_watchdog_state = WDOG_TIMEOUT_RESET;
- panic_halt_ipmi_set_timeout();
+ ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB);
}
}
return NOTIFY_OK;
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index f434856..a741e41 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -135,7 +135,6 @@
#include <asm/irq.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
/* if you have more than 8 printers, remember to increase LP_NO */
#define LP_NO 8
@@ -706,16 +705,13 @@ static long lp_compat_ioctl(struct file *file, unsigned int cmd,
{
unsigned int minor;
struct timeval par_timeout;
- struct compat_timeval __user *tc;
int ret;
minor = iminor(file->f_path.dentry->d_inode);
mutex_lock(&lp_mutex);
switch (cmd) {
case LPSETTIMEOUT:
- tc = compat_ptr(arg);
- if (get_user(par_timeout.tv_sec, &tc->tv_sec) ||
- get_user(par_timeout.tv_usec, &tc->tv_usec)) {
+ if (compat_get_timeval(&par_timeout, compat_ptr(arg))) {
ret = -EFAULT;
break;
}
diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c
index 1aeaaba..47ff7e4 100644
--- a/drivers/char/mbcs.c
+++ b/drivers/char/mbcs.c
@@ -28,7 +28,6 @@
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/sn/addrs.h>
#include <asm/sn/intr.h>
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c
index 5c0d96a..8b78750 100644
--- a/drivers/char/mspec.c
+++ b/drivers/char/mspec.c
@@ -44,7 +44,6 @@
#include <linux/slab.h>
#include <linux/numa.h>
#include <asm/page.h>
-#include <asm/system.h>
#include <asm/pgtable.h>
#include <linux/atomic.h>
#include <asm/tlbflush.h>
diff --git a/drivers/char/mwave/3780i.c b/drivers/char/mwave/3780i.c
index 492dbfb..881c9e5 100644
--- a/drivers/char/mwave/3780i.c
+++ b/drivers/char/mwave/3780i.c
@@ -56,7 +56,6 @@
#include <asm/io.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include "smapi.h"
#include "mwavedd.h"
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index eaade8a..9df78e2 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -111,7 +111,6 @@
#include <linux/uaccess.h>
#include <linux/mutex.h>
-#include <asm/system.h>
static DEFINE_MUTEX(nvram_mutex);
static DEFINE_SPINLOCK(nvram_state_lock);
diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c
index bf586ae..d45c334 100644
--- a/drivers/char/nwflash.c
+++ b/drivers/char/nwflash.c
@@ -32,7 +32,6 @@
#include <asm/io.h>
#include <asm/leds.h>
#include <asm/mach-types.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
/*****************************************************************************/
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index f6453df..0a484b4 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -60,7 +60,6 @@
#include <linux/ioctl.h>
#include <linux/synclink.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/dma.h>
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index 872e09a..af94374 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -83,7 +83,6 @@
#include <linux/ratelimit.h>
#include <asm/current.h>
-#include <asm/system.h>
#ifdef CONFIG_X86
#include <asm/hpet.h>
diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c
index 1ee8ce7..45713f0 100644
--- a/drivers/char/sonypi.c
+++ b/drivers/char/sonypi.c
@@ -54,7 +54,6 @@
#include <asm/uaccess.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <linux/sonypi.h>
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
index e90e1c7..31ba11c 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -89,7 +89,6 @@
#include <asm/io.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#ifdef CONFIG_OF
/* For open firmware. */
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 9b3cd08..165e1fe 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -8,3 +8,40 @@ config HAVE_CLK_PREPARE
config HAVE_MACH_CLKDEV
bool
+
+config COMMON_CLK
+ bool
+ select HAVE_CLK_PREPARE
+ ---help---
+ The common clock framework is a single definition of struct
+ clk, useful across many platforms, as well as an
+ implementation of the clock API in include/linux/clk.h.
+ Architectures utilizing the common struct clk should select
+ this option.
+
+menu "Common Clock Framework"
+ depends on COMMON_CLK
+
+config COMMON_CLK_DISABLE_UNUSED
+ bool "Disabled unused clocks at boot"
+ depends on COMMON_CLK
+ ---help---
+ Traverses the entire clock tree and disables any clocks that are
+ enabled in hardware but have not been enabled by any device drivers.
+ This saves power and keeps the software model of the clock in line
+ with reality.
+
+ If in doubt, say "N".
+
+config COMMON_CLK_DEBUG
+ bool "DebugFS representation of clock tree"
+ depends on COMMON_CLK
+ select DEBUG_FS
+ ---help---
+ Creates a directory hierchy in debugfs for visualizing the clk
+ tree structure. Each directory contains read-only members
+ that export information specific to that clk node: clk_rate,
+ clk_flags, clk_prepare_count, clk_enable_count &
+ clk_notifier_count.
+
+endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 07613fa..1f736bc 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,2 +1,4 @@
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
+obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \
+ clk-mux.o clk-divider.o
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
new file mode 100644
index 0000000..d5ac6a7
--- /dev/null
+++ b/drivers/clk/clk-divider.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Adjustable divider clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+
+/*
+ * DOC: basic adjustable divider clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is adjustable. clk->rate = parent->rate / divisor
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
+
+#define div_mask(d) ((1 << (d->width)) - 1)
+
+static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ unsigned int div;
+
+ div = readl(divider->reg) >> divider->shift;
+ div &= div_mask(divider);
+
+ if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
+ div++;
+
+ return parent_rate / div;
+}
+EXPORT_SYMBOL_GPL(clk_divider_recalc_rate);
+
+/*
+ * The reverse of DIV_ROUND_UP: The maximum number which
+ * divided by m is r
+ */
+#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
+
+static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
+ unsigned long *best_parent_rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ int i, bestdiv = 0;
+ unsigned long parent_rate, best = 0, now, maxdiv;
+
+ if (!rate)
+ rate = 1;
+
+ maxdiv = (1 << divider->width);
+
+ if (divider->flags & CLK_DIVIDER_ONE_BASED)
+ maxdiv--;
+
+ if (!best_parent_rate) {
+ parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
+ bestdiv = DIV_ROUND_UP(parent_rate, rate);
+ bestdiv = bestdiv == 0 ? 1 : bestdiv;
+ bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
+ return bestdiv;
+ }
+
+ /*
+ * The maximum divider we can use without overflowing
+ * unsigned long in rate * i below
+ */
+ maxdiv = min(ULONG_MAX / rate, maxdiv);
+
+ for (i = 1; i <= maxdiv; i++) {
+ parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
+ MULT_ROUND_UP(rate, i));
+ now = parent_rate / i;
+ if (now <= rate && now > best) {
+ bestdiv = i;
+ best = now;
+ *best_parent_rate = parent_rate;
+ }
+ }
+
+ if (!bestdiv) {
+ bestdiv = (1 << divider->width);
+ if (divider->flags & CLK_DIVIDER_ONE_BASED)
+ bestdiv--;
+ *best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
+ }
+
+ return bestdiv;
+}
+
+static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ int div;
+ div = clk_divider_bestdiv(hw, rate, prate);
+
+ if (prate)
+ return *prate / div;
+ else {
+ unsigned long r;
+ r = __clk_get_rate(__clk_get_parent(hw->clk));
+ return r / div;
+ }
+}
+EXPORT_SYMBOL_GPL(clk_divider_round_rate);
+
+static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate)
+{
+ struct clk_divider *divider = to_clk_divider(hw);
+ unsigned int div;
+ unsigned long flags = 0;
+ u32 val;
+
+ div = __clk_get_rate(__clk_get_parent(hw->clk)) / rate;
+
+ if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
+ div--;
+
+ if (div > div_mask(divider))
+ div = div_mask(divider);
+
+ if (divider->lock)
+ spin_lock_irqsave(divider->lock, flags);
+
+ val = readl(divider->reg);
+ val &= ~(div_mask(divider) << divider->shift);
+ val |= div << divider->shift;
+ writel(val, divider->reg);
+
+ if (divider->lock)
+ spin_unlock_irqrestore(divider->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(clk_divider_set_rate);
+
+struct clk_ops clk_divider_ops = {
+ .recalc_rate = clk_divider_recalc_rate,
+ .round_rate = clk_divider_round_rate,
+ .set_rate = clk_divider_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_divider_ops);
+
+struct clk *clk_register_divider(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags, spinlock_t *lock)
+{
+ struct clk_divider *div;
+ struct clk *clk;
+
+ div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
+
+ if (!div) {
+ pr_err("%s: could not allocate divider clk\n", __func__);
+ return NULL;
+ }
+
+ /* struct clk_divider assignments */
+ div->reg = reg;
+ div->shift = shift;
+ div->width = width;
+ div->flags = clk_divider_flags;
+ div->lock = lock;
+
+ if (parent_name) {
+ div->parent[0] = kstrdup(parent_name, GFP_KERNEL);
+ if (!div->parent[0])
+ goto out;
+ }
+
+ clk = clk_register(dev, name,
+ &clk_divider_ops, &div->hw,
+ div->parent,
+ (parent_name ? 1 : 0),
+ flags);
+ if (clk)
+ return clk;
+
+out:
+ kfree(div->parent[0]);
+ kfree(div);
+
+ return NULL;
+}
diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c
new file mode 100644
index 0000000..90c79fb
--- /dev/null
+++ b/drivers/clk/clk-fixed-rate.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Fixed rate clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+/*
+ * DOC: basic fixed-rate clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parents are prepared
+ * enable - clk_enable only ensures parents are enabled
+ * rate - rate is always a fixed value. No clk_set_rate support
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_fixed_rate(_hw) container_of(_hw, struct clk_fixed_rate, hw)
+
+static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return to_clk_fixed_rate(hw)->fixed_rate;
+}
+EXPORT_SYMBOL_GPL(clk_fixed_rate_recalc_rate);
+
+struct clk_ops clk_fixed_rate_ops = {
+ .recalc_rate = clk_fixed_rate_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_fixed_rate_ops);
+
+struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ unsigned long fixed_rate)
+{
+ struct clk_fixed_rate *fixed;
+ char **parent_names = NULL;
+ u8 len;
+
+ fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
+
+ if (!fixed) {
+ pr_err("%s: could not allocate fixed clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_fixed_rate assignments */
+ fixed->fixed_rate = fixed_rate;
+
+ if (parent_name) {
+ parent_names = kmalloc(sizeof(char *), GFP_KERNEL);
+
+ if (! parent_names)
+ goto out;
+
+ len = sizeof(char) * strlen(parent_name);
+
+ parent_names[0] = kmalloc(len, GFP_KERNEL);
+
+ if (!parent_names[0])
+ goto out;
+
+ strncpy(parent_names[0], parent_name, len);
+ }
+
+out:
+ return clk_register(dev, name,
+ &clk_fixed_rate_ops, &fixed->hw,
+ parent_names,
+ (parent_name ? 1 : 0),
+ flags);
+}
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
new file mode 100644
index 0000000..b5902e2
--- /dev/null
+++ b/drivers/clk/clk-gate.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Gated clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+
+/**
+ * DOC: basic gatable clock which can gate and ungate it's ouput
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control gating
+ * rate - inherits rate from parent. No clk_set_rate support
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
+
+static void clk_gate_set_bit(struct clk_gate *gate)
+{
+ u32 reg;
+ unsigned long flags = 0;
+
+ if (gate->lock)
+ spin_lock_irqsave(gate->lock, flags);
+
+ reg = readl(gate->reg);
+ reg |= BIT(gate->bit_idx);
+ writel(reg, gate->reg);
+
+ if (gate->lock)
+ spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static void clk_gate_clear_bit(struct clk_gate *gate)
+{
+ u32 reg;
+ unsigned long flags = 0;
+
+ if (gate->lock)
+ spin_lock_irqsave(gate->lock, flags);
+
+ reg = readl(gate->reg);
+ reg &= ~BIT(gate->bit_idx);
+ writel(reg, gate->reg);
+
+ if (gate->lock)
+ spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static int clk_gate_enable(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+
+ if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+ clk_gate_clear_bit(gate);
+ else
+ clk_gate_set_bit(gate);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(clk_gate_enable);
+
+static void clk_gate_disable(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+
+ if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+ clk_gate_set_bit(gate);
+ else
+ clk_gate_clear_bit(gate);
+}
+EXPORT_SYMBOL_GPL(clk_gate_disable);
+
+static int clk_gate_is_enabled(struct clk_hw *hw)
+{
+ u32 reg;
+ struct clk_gate *gate = to_clk_gate(hw);
+
+ reg = readl(gate->reg);
+
+ /* if a set bit disables this clk, flip it before masking */
+ if (gate->flags & CLK_GATE_SET_TO_DISABLE)
+ reg ^= BIT(gate->bit_idx);
+
+ reg &= BIT(gate->bit_idx);
+
+ return reg ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(clk_gate_is_enabled);
+
+struct clk_ops clk_gate_ops = {
+ .enable = clk_gate_enable,
+ .disable = clk_gate_disable,
+ .is_enabled = clk_gate_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_gate_ops);
+
+struct clk *clk_register_gate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 bit_idx,
+ u8 clk_gate_flags, spinlock_t *lock)
+{
+ struct clk_gate *gate;
+ struct clk *clk;
+
+ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+
+ if (!gate) {
+ pr_err("%s: could not allocate gated clk\n", __func__);
+ return NULL;
+ }
+
+ /* struct clk_gate assignments */
+ gate->reg = reg;
+ gate->bit_idx = bit_idx;
+ gate->flags = clk_gate_flags;
+ gate->lock = lock;
+
+ if (parent_name) {
+ gate->parent[0] = kstrdup(parent_name, GFP_KERNEL);
+ if (!gate->parent[0])
+ goto out;
+ }
+
+ clk = clk_register(dev, name,
+ &clk_gate_ops, &gate->hw,
+ gate->parent,
+ (parent_name ? 1 : 0),
+ flags);
+ if (clk)
+ return clk;
+out:
+ kfree(gate->parent[0]);
+ kfree(gate);
+
+ return NULL;
+}
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
new file mode 100644
index 0000000..c71ad1f
--- /dev/null
+++ b/drivers/clk/clk-mux.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Simple multiplexer clock implementation
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+/*
+ * DOC: basic adjustable multiplexer clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is only affected by parent switching. No clk_set_rate support
+ * parent - parent is adjustable through clk_set_parent
+ */
+
+#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
+
+static u8 clk_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val;
+
+ /*
+ * FIXME need a mux-specific flag to determine if val is bitwise or numeric
+ * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
+ * to 0x7 (index starts at one)
+ * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
+ * val = 0x4 really means "bit 2, index starts at bit 0"
+ */
+ val = readl(mux->reg) >> mux->shift;
+ val &= (1 << mux->width) - 1;
+
+ if (val && (mux->flags & CLK_MUX_INDEX_BIT))
+ val = ffs(val) - 1;
+
+ if (val && (mux->flags & CLK_MUX_INDEX_ONE))
+ val--;
+
+ if (val >= __clk_get_num_parents(hw->clk))
+ return -EINVAL;
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(clk_mux_get_parent);
+
+static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val;
+ unsigned long flags = 0;
+
+ if (mux->flags & CLK_MUX_INDEX_BIT)
+ index = (1 << ffs(index));
+
+ if (mux->flags & CLK_MUX_INDEX_ONE)
+ index++;
+
+ if (mux->lock)
+ spin_lock_irqsave(mux->lock, flags);
+
+ val = readl(mux->reg);
+ val &= ~(((1 << mux->width) - 1) << mux->shift);
+ val |= index << mux->shift;
+ writel(val, mux->reg);
+
+ if (mux->lock)
+ spin_unlock_irqrestore(mux->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(clk_mux_set_parent);
+
+struct clk_ops clk_mux_ops = {
+ .get_parent = clk_mux_get_parent,
+ .set_parent = clk_mux_set_parent,
+};
+EXPORT_SYMBOL_GPL(clk_mux_ops);
+
+struct clk *clk_register_mux(struct device *dev, const char *name,
+ char **parent_names, u8 num_parents, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_mux_flags, spinlock_t *lock)
+{
+ struct clk_mux *mux;
+
+ mux = kmalloc(sizeof(struct clk_mux), GFP_KERNEL);
+
+ if (!mux) {
+ pr_err("%s: could not allocate mux clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_mux assignments */
+ mux->reg = reg;
+ mux->shift = shift;
+ mux->width = width;
+ mux->flags = clk_mux_flags;
+ mux->lock = lock;
+
+ return clk_register(dev, name, &clk_mux_ops, &mux->hw,
+ parent_names, num_parents, flags);
+}
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
new file mode 100644
index 0000000..9cf6f59e
--- /dev/null
+++ b/drivers/clk/clk.c
@@ -0,0 +1,1461 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Linaro Ltd <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Standard functionality for the common clock API. See Documentation/clk.txt
+ */
+
+#include <linux/clk-private.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+static DEFINE_SPINLOCK(enable_lock);
+static DEFINE_MUTEX(prepare_lock);
+
+static HLIST_HEAD(clk_root_list);
+static HLIST_HEAD(clk_orphan_list);
+static LIST_HEAD(clk_notifier_list);
+
+/*** debugfs support ***/
+
+#ifdef CONFIG_COMMON_CLK_DEBUG
+#include <linux/debugfs.h>
+
+static struct dentry *rootdir;
+static struct dentry *orphandir;
+static int inited = 0;
+
+/* caller must hold prepare_lock */
+static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
+{
+ struct dentry *d;
+ int ret = -ENOMEM;
+
+ if (!clk || !pdentry) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ d = debugfs_create_dir(clk->name, pdentry);
+ if (!d)
+ goto out;
+
+ clk->dentry = d;
+
+ d = debugfs_create_u32("clk_rate", S_IRUGO, clk->dentry,
+ (u32 *)&clk->rate);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry,
+ (u32 *)&clk->flags);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_u32("clk_prepare_count", S_IRUGO, clk->dentry,
+ (u32 *)&clk->prepare_count);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_u32("clk_enable_count", S_IRUGO, clk->dentry,
+ (u32 *)&clk->enable_count);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_u32("clk_notifier_count", S_IRUGO, clk->dentry,
+ (u32 *)&clk->notifier_count);
+ if (!d)
+ goto err_out;
+
+ ret = 0;
+ goto out;
+
+err_out:
+ debugfs_remove(clk->dentry);
+out:
+ return ret;
+}
+
+/* caller must hold prepare_lock */
+static int clk_debug_create_subtree(struct clk *clk, struct dentry *pdentry)
+{
+ struct clk *child;
+ struct hlist_node *tmp;
+ int ret = -EINVAL;;
+
+ if (!clk || !pdentry)
+ goto out;
+
+ ret = clk_debug_create_one(clk, pdentry);
+
+ if (ret)
+ goto out;
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node)
+ clk_debug_create_subtree(child, clk->dentry);
+
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * clk_debug_register - add a clk node to the debugfs clk tree
+ * @clk: the clk being added to the debugfs clk tree
+ *
+ * Dynamically adds a clk to the debugfs clk tree if debugfs has been
+ * initialized. Otherwise it bails out early since the debugfs clk tree
+ * will be created lazily by clk_debug_init as part of a late_initcall.
+ *
+ * Caller must hold prepare_lock. Only clk_init calls this function (so
+ * far) so this is taken care.
+ */
+static int clk_debug_register(struct clk *clk)
+{
+ struct clk *parent;
+ struct dentry *pdentry;
+ int ret = 0;
+
+ if (!inited)
+ goto out;
+
+ parent = clk->parent;
+
+ /*
+ * Check to see if a clk is a root clk. Also check that it is
+ * safe to add this clk to debugfs
+ */
+ if (!parent)
+ if (clk->flags & CLK_IS_ROOT)
+ pdentry = rootdir;
+ else
+ pdentry = orphandir;
+ else
+ if (parent->dentry)
+ pdentry = parent->dentry;
+ else
+ goto out;
+
+ ret = clk_debug_create_subtree(clk, pdentry);
+
+out:
+ return ret;
+}
+
+/**
+ * clk_debug_init - lazily create the debugfs clk tree visualization
+ *
+ * clks are often initialized very early during boot before memory can
+ * be dynamically allocated and well before debugfs is setup.
+ * clk_debug_init walks the clk tree hierarchy while holding
+ * prepare_lock and creates the topology as part of a late_initcall,
+ * thus insuring that clks initialized very early will still be
+ * represented in the debugfs clk tree. This function should only be
+ * called once at boot-time, and all other clks added dynamically will
+ * be done so with clk_debug_register.
+ */
+static int __init clk_debug_init(void)
+{
+ struct clk *clk;
+ struct hlist_node *tmp;
+
+ rootdir = debugfs_create_dir("clk", NULL);
+
+ if (!rootdir)
+ return -ENOMEM;
+
+ orphandir = debugfs_create_dir("orphans", rootdir);
+
+ if (!orphandir)
+ return -ENOMEM;
+
+ mutex_lock(&prepare_lock);
+
+ hlist_for_each_entry(clk, tmp, &clk_root_list, child_node)
+ clk_debug_create_subtree(clk, rootdir);
+
+ hlist_for_each_entry(clk, tmp, &clk_orphan_list, child_node)
+ clk_debug_create_subtree(clk, orphandir);
+
+ inited = 1;
+
+ mutex_unlock(&prepare_lock);
+
+ return 0;
+}
+late_initcall(clk_debug_init);
+#else
+static inline int clk_debug_register(struct clk *clk) { return 0; }
+#endif /* CONFIG_COMMON_CLK_DEBUG */
+
+#ifdef CONFIG_COMMON_CLK_DISABLE_UNUSED
+/* caller must hold prepare_lock */
+static void clk_disable_unused_subtree(struct clk *clk)
+{
+ struct clk *child;
+ struct hlist_node *tmp;
+ unsigned long flags;
+
+ if (!clk)
+ goto out;
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node)
+ clk_disable_unused_subtree(child);
+
+ spin_lock_irqsave(&enable_lock, flags);
+
+ if (clk->enable_count)
+ goto unlock_out;
+
+ if (clk->flags & CLK_IGNORE_UNUSED)
+ goto unlock_out;
+
+ if (__clk_is_enabled(clk) && clk->ops->disable)
+ clk->ops->disable(clk->hw);
+
+unlock_out:
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+out:
+ return;
+}
+
+static int clk_disable_unused(void)
+{
+ struct clk *clk;
+ struct hlist_node *tmp;
+
+ mutex_lock(&prepare_lock);
+
+ hlist_for_each_entry(clk, tmp, &clk_root_list, child_node)
+ clk_disable_unused_subtree(clk);
+
+ hlist_for_each_entry(clk, tmp, &clk_orphan_list, child_node)
+ clk_disable_unused_subtree(clk);
+
+ mutex_unlock(&prepare_lock);
+
+ return 0;
+}
+late_initcall(clk_disable_unused);
+#else
+static inline int clk_disable_unused(struct clk *clk) { return 0; }
+#endif /* CONFIG_COMMON_CLK_DISABLE_UNUSED */
+
+/*** helper functions ***/
+
+inline const char *__clk_get_name(struct clk *clk)
+{
+ return !clk ? NULL : clk->name;
+}
+
+inline struct clk_hw *__clk_get_hw(struct clk *clk)
+{
+ return !clk ? NULL : clk->hw;
+}
+
+inline u8 __clk_get_num_parents(struct clk *clk)
+{
+ return !clk ? -EINVAL : clk->num_parents;
+}
+
+inline struct clk *__clk_get_parent(struct clk *clk)
+{
+ return !clk ? NULL : clk->parent;
+}
+
+inline int __clk_get_enable_count(struct clk *clk)
+{
+ return !clk ? -EINVAL : clk->enable_count;
+}
+
+inline int __clk_get_prepare_count(struct clk *clk)
+{
+ return !clk ? -EINVAL : clk->prepare_count;
+}
+
+unsigned long __clk_get_rate(struct clk *clk)
+{
+ unsigned long ret;
+
+ if (!clk) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = clk->rate;
+
+ if (clk->flags & CLK_IS_ROOT)
+ goto out;
+
+ if (!clk->parent)
+ ret = -ENODEV;
+
+out:
+ return ret;
+}
+
+inline unsigned long __clk_get_flags(struct clk *clk)
+{
+ return !clk ? -EINVAL : clk->flags;
+}
+
+int __clk_is_enabled(struct clk *clk)
+{
+ int ret;
+
+ if (!clk)
+ return -EINVAL;
+
+ /*
+ * .is_enabled is only mandatory for clocks that gate
+ * fall back to software usage counter if .is_enabled is missing
+ */
+ if (!clk->ops->is_enabled) {
+ ret = clk->enable_count ? 1 : 0;
+ goto out;
+ }
+
+ ret = clk->ops->is_enabled(clk->hw);
+out:
+ return ret;
+}
+
+static struct clk *__clk_lookup_subtree(const char *name, struct clk *clk)
+{
+ struct clk *child;
+ struct clk *ret;
+ struct hlist_node *tmp;
+
+ if (!strcmp(clk->name, name))
+ return clk;
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node) {
+ ret = __clk_lookup_subtree(name, child);
+ if (ret)
+ return ret;
+ }
+
+ return NULL;
+}
+
+struct clk *__clk_lookup(const char *name)
+{
+ struct clk *root_clk;
+ struct clk *ret;
+ struct hlist_node *tmp;
+
+ if (!name)
+ return NULL;
+
+ /* search the 'proper' clk tree first */
+ hlist_for_each_entry(root_clk, tmp, &clk_root_list, child_node) {
+ ret = __clk_lookup_subtree(name, root_clk);
+ if (ret)
+ return ret;
+ }
+
+ /* if not found, then search the orphan tree */
+ hlist_for_each_entry(root_clk, tmp, &clk_orphan_list, child_node) {
+ ret = __clk_lookup_subtree(name, root_clk);
+ if (ret)
+ return ret;
+ }
+
+ return NULL;
+}
+
+/*** clk api ***/
+
+void __clk_unprepare(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ if (WARN_ON(clk->prepare_count == 0))
+ return;
+
+ if (--clk->prepare_count > 0)
+ return;
+
+ WARN_ON(clk->enable_count > 0);
+
+ if (clk->ops->unprepare)
+ clk->ops->unprepare(clk->hw);
+
+ __clk_unprepare(clk->parent);
+}
+
+/**
+ * clk_unprepare - undo preparation of a clock source
+ * @clk: the clk being unprepare
+ *
+ * clk_unprepare may sleep, which differentiates it from clk_disable. In a
+ * simple case, clk_unprepare can be used instead of clk_disable to gate a clk
+ * if the operation may sleep. One example is a clk which is accessed over
+ * I2c. In the complex case a clk gate operation may require a fast and a slow
+ * part. It is this reason that clk_unprepare and clk_disable are not mutually
+ * exclusive. In fact clk_disable must be called before clk_unprepare.
+ */
+void clk_unprepare(struct clk *clk)
+{
+ mutex_lock(&prepare_lock);
+ __clk_unprepare(clk);
+ mutex_unlock(&prepare_lock);
+}
+EXPORT_SYMBOL_GPL(clk_unprepare);
+
+int __clk_prepare(struct clk *clk)
+{
+ int ret = 0;
+
+ if (!clk)
+ return 0;
+
+ if (clk->prepare_count == 0) {
+ ret = __clk_prepare(clk->parent);
+ if (ret)
+ return ret;
+
+ if (clk->ops->prepare) {
+ ret = clk->ops->prepare(clk->hw);
+ if (ret) {
+ __clk_unprepare(clk->parent);
+ return ret;
+ }
+ }
+ }
+
+ clk->prepare_count++;
+
+ return 0;
+}
+
+/**
+ * clk_prepare - prepare a clock source
+ * @clk: the clk being prepared
+ *
+ * clk_prepare may sleep, which differentiates it from clk_enable. In a simple
+ * case, clk_prepare can be used instead of clk_enable to ungate a clk if the
+ * operation may sleep. One example is a clk which is accessed over I2c. In
+ * the complex case a clk ungate operation may require a fast and a slow part.
+ * It is this reason that clk_prepare and clk_enable are not mutually
+ * exclusive. In fact clk_prepare must be called before clk_enable.
+ * Returns 0 on success, -EERROR otherwise.
+ */
+int clk_prepare(struct clk *clk)
+{
+ int ret;
+
+ mutex_lock(&prepare_lock);
+ ret = __clk_prepare(clk);
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_prepare);
+
+static void __clk_disable(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ if (WARN_ON(clk->enable_count == 0))
+ return;
+
+ if (--clk->enable_count > 0)
+ return;
+
+ if (clk->ops->disable)
+ clk->ops->disable(clk->hw);
+
+ __clk_disable(clk->parent);
+}
+
+/**
+ * clk_disable - gate a clock
+ * @clk: the clk being gated
+ *
+ * clk_disable must not sleep, which differentiates it from clk_unprepare. In
+ * a simple case, clk_disable can be used instead of clk_unprepare to gate a
+ * clk if the operation is fast and will never sleep. One example is a
+ * SoC-internal clk which is controlled via simple register writes. In the
+ * complex case a clk gate operation may require a fast and a slow part. It is
+ * this reason that clk_unprepare and clk_disable are not mutually exclusive.
+ * In fact clk_disable must be called before clk_unprepare.
+ */
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&enable_lock, flags);
+ __clk_disable(clk);
+ spin_unlock_irqrestore(&enable_lock, flags);
+}
+EXPORT_SYMBOL_GPL(clk_disable);
+
+static int __clk_enable(struct clk *clk)
+{
+ int ret = 0;
+
+ if (!clk)
+ return 0;
+
+ if (WARN_ON(clk->prepare_count == 0))
+ return -ESHUTDOWN;
+
+ if (clk->enable_count == 0) {
+ ret = __clk_enable(clk->parent);
+
+ if (ret)
+ return ret;
+
+ if (clk->ops->enable) {
+ ret = clk->ops->enable(clk->hw);
+ if (ret) {
+ __clk_disable(clk->parent);
+ return ret;
+ }
+ }
+ }
+
+ clk->enable_count++;
+ return 0;
+}
+
+/**
+ * clk_enable - ungate a clock
+ * @clk: the clk being ungated
+ *
+ * clk_enable must not sleep, which differentiates it from clk_prepare. In a
+ * simple case, clk_enable can be used instead of clk_prepare to ungate a clk
+ * if the operation will never sleep. One example is a SoC-internal clk which
+ * is controlled via simple register writes. In the complex case a clk ungate
+ * operation may require a fast and a slow part. It is this reason that
+ * clk_enable and clk_prepare are not mutually exclusive. In fact clk_prepare
+ * must be called before clk_enable. Returns 0 on success, -EERROR
+ * otherwise.
+ */
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&enable_lock, flags);
+ ret = __clk_enable(clk);
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_enable);
+
+/**
+ * clk_get_rate - return the rate of clk
+ * @clk: the clk whose rate is being returned
+ *
+ * Simply returns the cached rate of the clk. Does not query the hardware. If
+ * clk is NULL then returns -EINVAL.
+ */
+unsigned long clk_get_rate(struct clk *clk)
+{
+ unsigned long rate;
+
+ mutex_lock(&prepare_lock);
+ rate = __clk_get_rate(clk);
+ mutex_unlock(&prepare_lock);
+
+ return rate;
+}
+EXPORT_SYMBOL_GPL(clk_get_rate);
+
+/**
+ * __clk_round_rate - round the given rate for a clk
+ * @clk: round the rate of this clock
+ *
+ * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate
+ */
+unsigned long __clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long unused;
+
+ if (!clk)
+ return -EINVAL;
+
+ if (!clk->ops->round_rate)
+ return clk->rate;
+
+ if (clk->flags & CLK_SET_RATE_PARENT)
+ return clk->ops->round_rate(clk->hw, rate, &unused);
+ else
+ return clk->ops->round_rate(clk->hw, rate, NULL);
+}
+
+/**
+ * clk_round_rate - round the given rate for a clk
+ * @clk: the clk for which we are rounding a rate
+ * @rate: the rate which is to be rounded
+ *
+ * Takes in a rate as input and rounds it to a rate that the clk can actually
+ * use which is then returned. If clk doesn't support round_rate operation
+ * then the parent rate is returned.
+ */
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long ret;
+
+ mutex_lock(&prepare_lock);
+ ret = __clk_round_rate(clk, rate);
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_round_rate);
+
+/**
+ * __clk_notify - call clk notifier chain
+ * @clk: struct clk * that is changing rate
+ * @msg: clk notifier type (see include/linux/clk.h)
+ * @old_rate: old clk rate
+ * @new_rate: new clk rate
+ *
+ * Triggers a notifier call chain on the clk rate-change notification
+ * for 'clk'. Passes a pointer to the struct clk and the previous
+ * and current rates to the notifier callback. Intended to be called by
+ * internal clock code only. Returns NOTIFY_DONE from the last driver
+ * called if all went well, or NOTIFY_STOP or NOTIFY_BAD immediately if
+ * a driver returns that.
+ */
+static int __clk_notify(struct clk *clk, unsigned long msg,
+ unsigned long old_rate, unsigned long new_rate)
+{
+ struct clk_notifier *cn;
+ struct clk_notifier_data cnd;
+ int ret = NOTIFY_DONE;
+
+ cnd.clk = clk;
+ cnd.old_rate = old_rate;
+ cnd.new_rate = new_rate;
+
+ list_for_each_entry(cn, &clk_notifier_list, node) {
+ if (cn->clk == clk) {
+ ret = srcu_notifier_call_chain(&cn->notifier_head, msg,
+ &cnd);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * __clk_recalc_rates
+ * @clk: first clk in the subtree
+ * @msg: notification type (see include/linux/clk.h)
+ *
+ * Walks the subtree of clks starting with clk and recalculates rates as it
+ * goes. Note that if a clk does not implement the .recalc_rate callback then
+ * it is assumed that the clock will take on the rate of it's parent.
+ *
+ * clk_recalc_rates also propagates the POST_RATE_CHANGE notification,
+ * if necessary.
+ *
+ * Caller must hold prepare_lock.
+ */
+static void __clk_recalc_rates(struct clk *clk, unsigned long msg)
+{
+ unsigned long old_rate;
+ unsigned long parent_rate = 0;
+ struct hlist_node *tmp;
+ struct clk *child;
+
+ old_rate = clk->rate;
+
+ if (clk->parent)
+ parent_rate = clk->parent->rate;
+
+ if (clk->ops->recalc_rate)
+ clk->rate = clk->ops->recalc_rate(clk->hw, parent_rate);
+ else
+ clk->rate = parent_rate;
+
+ /*
+ * ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE
+ * & ABORT_RATE_CHANGE notifiers
+ */
+ if (clk->notifier_count && msg)
+ __clk_notify(clk, msg, old_rate, clk->rate);
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node)
+ __clk_recalc_rates(child, msg);
+}
+
+/**
+ * __clk_speculate_rates
+ * @clk: first clk in the subtree
+ * @parent_rate: the "future" rate of clk's parent
+ *
+ * Walks the subtree of clks starting with clk, speculating rates as it
+ * goes and firing off PRE_RATE_CHANGE notifications as necessary.
+ *
+ * Unlike clk_recalc_rates, clk_speculate_rates exists only for sending
+ * pre-rate change notifications and returns early if no clks in the
+ * subtree have subscribed to the notifications. Note that if a clk does not
+ * implement the .recalc_rate callback then it is assumed that the clock will
+ * take on the rate of it's parent.
+ *
+ * Caller must hold prepare_lock.
+ */
+static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate)
+{
+ struct hlist_node *tmp;
+ struct clk *child;
+ unsigned long new_rate;
+ int ret = NOTIFY_DONE;
+
+ if (clk->ops->recalc_rate)
+ new_rate = clk->ops->recalc_rate(clk->hw, parent_rate);
+ else
+ new_rate = parent_rate;
+
+ /* abort the rate change if a driver returns NOTIFY_BAD */
+ if (clk->notifier_count)
+ ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate);
+
+ if (ret == NOTIFY_BAD)
+ goto out;
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node) {
+ ret = __clk_speculate_rates(child, new_rate);
+ if (ret == NOTIFY_BAD)
+ break;
+ }
+
+out:
+ return ret;
+}
+
+static void clk_calc_subtree(struct clk *clk, unsigned long new_rate)
+{
+ struct clk *child;
+ struct hlist_node *tmp;
+
+ clk->new_rate = new_rate;
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node) {
+ if (child->ops->recalc_rate)
+ child->new_rate = child->ops->recalc_rate(child->hw, new_rate);
+ else
+ child->new_rate = new_rate;
+ clk_calc_subtree(child, child->new_rate);
+ }
+}
+
+/*
+ * calculate the new rates returning the topmost clock that has to be
+ * changed.
+ */
+static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate)
+{
+ struct clk *top = clk;
+ unsigned long best_parent_rate = clk->parent->rate;
+ unsigned long new_rate;
+
+ if (!clk->ops->round_rate && !(clk->flags & CLK_SET_RATE_PARENT)) {
+ clk->new_rate = clk->rate;
+ return NULL;
+ }
+
+ if (!clk->ops->round_rate && (clk->flags & CLK_SET_RATE_PARENT)) {
+ top = clk_calc_new_rates(clk->parent, rate);
+ new_rate = clk->new_rate = clk->parent->new_rate;
+
+ goto out;
+ }
+
+ if (clk->flags & CLK_SET_RATE_PARENT)
+ new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate);
+ else
+ new_rate = clk->ops->round_rate(clk->hw, rate, NULL);
+
+ if (best_parent_rate != clk->parent->rate) {
+ top = clk_calc_new_rates(clk->parent, best_parent_rate);
+
+ goto out;
+ }
+
+out:
+ clk_calc_subtree(clk, new_rate);
+
+ return top;
+}
+
+/*
+ * Notify about rate changes in a subtree. Always walk down the whole tree
+ * so that in case of an error we can walk down the whole tree again and
+ * abort the change.
+ */
+static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event)
+{
+ struct hlist_node *tmp;
+ struct clk *child, *fail_clk = NULL;
+ int ret = NOTIFY_DONE;
+
+ if (clk->rate == clk->new_rate)
+ return 0;
+
+ if (clk->notifier_count) {
+ ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
+ if (ret == NOTIFY_BAD)
+ fail_clk = clk;
+ }
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node) {
+ clk = clk_propagate_rate_change(child, event);
+ if (clk)
+ fail_clk = clk;
+ }
+
+ return fail_clk;
+}
+
+/*
+ * walk down a subtree and set the new rates notifying the rate
+ * change on the way
+ */
+static void clk_change_rate(struct clk *clk)
+{
+ struct clk *child;
+ unsigned long old_rate;
+ struct hlist_node *tmp;
+
+ old_rate = clk->rate;
+
+ if (clk->ops->set_rate)
+ clk->ops->set_rate(clk->hw, clk->new_rate);
+
+ if (clk->ops->recalc_rate)
+ clk->rate = clk->ops->recalc_rate(clk->hw,
+ clk->parent->rate);
+ else
+ clk->rate = clk->parent->rate;
+
+ if (clk->notifier_count && old_rate != clk->rate)
+ __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node)
+ clk_change_rate(child);
+}
+
+/**
+ * clk_set_rate - specify a new rate for clk
+ * @clk: the clk whose rate is being changed
+ * @rate: the new rate for clk
+ *
+ * In the simplest case clk_set_rate will only change the rate of clk.
+ *
+ * If clk has the CLK_SET_RATE_GATE flag set and it is enabled this call
+ * will fail; only when the clk is disabled will it be able to change
+ * its rate.
+ *
+ * Setting the CLK_SET_RATE_PARENT flag allows clk_set_rate to
+ * recursively propagate up to clk's parent; whether or not this happens
+ * depends on the outcome of clk's .round_rate implementation. If
+ * *parent_rate is 0 after calling .round_rate then upstream parent
+ * propagation is ignored. If *parent_rate comes back with a new rate
+ * for clk's parent then we propagate up to clk's parent and set it's
+ * rate. Upward propagation will continue until either a clk does not
+ * support the CLK_SET_RATE_PARENT flag or .round_rate stops requesting
+ * changes to clk's parent_rate. If there is a failure during upstream
+ * propagation then clk_set_rate will unwind and restore each clk's rate
+ * that had been successfully changed. Afterwards a rate change abort
+ * notification will be propagated downstream, starting from the clk
+ * that failed.
+ *
+ * At the end of all of the rate setting, clk_set_rate internally calls
+ * __clk_recalc_rates and propagates the rate changes downstream,
+ * starting from the highest clk whose rate was changed. This has the
+ * added benefit of propagating post-rate change notifiers.
+ *
+ * Note that while post-rate change and rate change abort notifications
+ * are guaranteed to be sent to a clk only once per call to
+ * clk_set_rate, pre-change notifications will be sent for every clk
+ * whose rate is changed. Stacking pre-change notifications is noisy
+ * for the drivers subscribed to them, but this allows drivers to react
+ * to intermediate clk rate changes up until the point where the final
+ * rate is achieved at the end of upstream propagation.
+ *
+ * Returns 0 on success, -EERROR otherwise.
+ */
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct clk *top, *fail_clk;
+ int ret = 0;
+
+ /* prevent racing with updates to the clock topology */
+ mutex_lock(&prepare_lock);
+
+ /* bail early if nothing to do */
+ if (rate == clk->rate)
+ goto out;
+
+ /* calculate new rates and get the topmost changed clock */
+ top = clk_calc_new_rates(clk, rate);
+ if (!top) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* notify that we are about to change rates */
+ fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
+ if (fail_clk) {
+ pr_warn("%s: failed to set %s rate\n", __func__,
+ fail_clk->name);
+ clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* change the rates */
+ clk_change_rate(top);
+
+ mutex_unlock(&prepare_lock);
+
+ return 0;
+out:
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_rate);
+
+/**
+ * clk_get_parent - return the parent of a clk
+ * @clk: the clk whose parent gets returned
+ *
+ * Simply returns clk->parent. Returns NULL if clk is NULL.
+ */
+struct clk *clk_get_parent(struct clk *clk)
+{
+ struct clk *parent;
+
+ mutex_lock(&prepare_lock);
+ parent = __clk_get_parent(clk);
+ mutex_unlock(&prepare_lock);
+
+ return parent;
+}
+EXPORT_SYMBOL_GPL(clk_get_parent);
+
+/*
+ * .get_parent is mandatory for clocks with multiple possible parents. It is
+ * optional for single-parent clocks. Always call .get_parent if it is
+ * available and WARN if it is missing for multi-parent clocks.
+ *
+ * For single-parent clocks without .get_parent, first check to see if the
+ * .parents array exists, and if so use it to avoid an expensive tree
+ * traversal. If .parents does not exist then walk the tree with __clk_lookup.
+ */
+static struct clk *__clk_init_parent(struct clk *clk)
+{
+ struct clk *ret = NULL;
+ u8 index;
+
+ /* handle the trivial cases */
+
+ if (!clk->num_parents)
+ goto out;
+
+ if (clk->num_parents == 1) {
+ if (IS_ERR_OR_NULL(clk->parent))
+ ret = clk->parent = __clk_lookup(clk->parent_names[0]);
+ ret = clk->parent;
+ goto out;
+ }
+
+ if (!clk->ops->get_parent) {
+ WARN(!clk->ops->get_parent,
+ "%s: multi-parent clocks must implement .get_parent\n",
+ __func__);
+ goto out;
+ };
+
+ /*
+ * Do our best to cache parent clocks in clk->parents. This prevents
+ * unnecessary and expensive calls to __clk_lookup. We don't set
+ * clk->parent here; that is done by the calling function
+ */
+
+ index = clk->ops->get_parent(clk->hw);
+
+ if (!clk->parents)
+ clk->parents =
+ kmalloc((sizeof(struct clk*) * clk->num_parents),
+ GFP_KERNEL);
+
+ if (!clk->parents)
+ ret = __clk_lookup(clk->parent_names[index]);
+ else if (!clk->parents[index])
+ ret = clk->parents[index] =
+ __clk_lookup(clk->parent_names[index]);
+ else
+ ret = clk->parents[index];
+
+out:
+ return ret;
+}
+
+void __clk_reparent(struct clk *clk, struct clk *new_parent)
+{
+#ifdef CONFIG_COMMON_CLK_DEBUG
+ struct dentry *d;
+ struct dentry *new_parent_d;
+#endif
+
+ if (!clk || !new_parent)
+ return;
+
+ hlist_del(&clk->child_node);
+
+ if (new_parent)
+ hlist_add_head(&clk->child_node, &new_parent->children);
+ else
+ hlist_add_head(&clk->child_node, &clk_orphan_list);
+
+#ifdef CONFIG_COMMON_CLK_DEBUG
+ if (!inited)
+ goto out;
+
+ if (new_parent)
+ new_parent_d = new_parent->dentry;
+ else
+ new_parent_d = orphandir;
+
+ d = debugfs_rename(clk->dentry->d_parent, clk->dentry,
+ new_parent_d, clk->name);
+ if (d)
+ clk->dentry = d;
+ else
+ pr_debug("%s: failed to rename debugfs entry for %s\n",
+ __func__, clk->name);
+out:
+#endif
+
+ clk->parent = new_parent;
+
+ __clk_recalc_rates(clk, POST_RATE_CHANGE);
+}
+
+static int __clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk *old_parent;
+ unsigned long flags;
+ int ret = -EINVAL;
+ u8 i;
+
+ old_parent = clk->parent;
+
+ /* find index of new parent clock using cached parent ptrs */
+ for (i = 0; i < clk->num_parents; i++)
+ if (clk->parents[i] == parent)
+ break;
+
+ /*
+ * find index of new parent clock using string name comparison
+ * also try to cache the parent to avoid future calls to __clk_lookup
+ */
+ if (i == clk->num_parents)
+ for (i = 0; i < clk->num_parents; i++)
+ if (!strcmp(clk->parent_names[i], parent->name)) {
+ clk->parents[i] = __clk_lookup(parent->name);
+ break;
+ }
+
+ if (i == clk->num_parents) {
+ pr_debug("%s: clock %s is not a possible parent of clock %s\n",
+ __func__, parent->name, clk->name);
+ goto out;
+ }
+
+ /* migrate prepare and enable */
+ if (clk->prepare_count)
+ __clk_prepare(parent);
+
+ /* FIXME replace with clk_is_enabled(clk) someday */
+ spin_lock_irqsave(&enable_lock, flags);
+ if (clk->enable_count)
+ __clk_enable(parent);
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+ /* change clock input source */
+ ret = clk->ops->set_parent(clk->hw, i);
+
+ /* clean up old prepare and enable */
+ spin_lock_irqsave(&enable_lock, flags);
+ if (clk->enable_count)
+ __clk_disable(old_parent);
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+ if (clk->prepare_count)
+ __clk_unprepare(old_parent);
+
+out:
+ return ret;
+}
+
+/**
+ * clk_set_parent - switch the parent of a mux clk
+ * @clk: the mux clk whose input we are switching
+ * @parent: the new input to clk
+ *
+ * Re-parent clk to use parent as it's new input source. If clk has the
+ * CLK_SET_PARENT_GATE flag set then clk must be gated for this
+ * operation to succeed. After successfully changing clk's parent
+ * clk_set_parent will update the clk topology, sysfs topology and
+ * propagate rate recalculation via __clk_recalc_rates. Returns 0 on
+ * success, -EERROR otherwise.
+ */
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int ret = 0;
+
+ if (!clk || !clk->ops)
+ return -EINVAL;
+
+ if (!clk->ops->set_parent)
+ return -ENOSYS;
+
+ /* prevent racing with updates to the clock topology */
+ mutex_lock(&prepare_lock);
+
+ if (clk->parent == parent)
+ goto out;
+
+ /* propagate PRE_RATE_CHANGE notifications */
+ if (clk->notifier_count)
+ ret = __clk_speculate_rates(clk, parent->rate);
+
+ /* abort if a driver objects */
+ if (ret == NOTIFY_STOP)
+ goto out;
+
+ /* only re-parent if the clock is not in use */
+ if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count)
+ ret = -EBUSY;
+ else
+ ret = __clk_set_parent(clk, parent);
+
+ /* propagate ABORT_RATE_CHANGE if .set_parent failed */
+ if (ret) {
+ __clk_recalc_rates(clk, ABORT_RATE_CHANGE);
+ goto out;
+ }
+
+ /* propagate rate recalculation downstream */
+ __clk_reparent(clk, parent);
+
+out:
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_parent);
+
+/**
+ * __clk_init - initialize the data structures in a struct clk
+ * @dev: device initializing this clk, placeholder for now
+ * @clk: clk being initialized
+ *
+ * Initializes the lists in struct clk, queries the hardware for the
+ * parent and rate and sets them both.
+ *
+ * Any struct clk passed into __clk_init must have the following members
+ * populated:
+ * .name
+ * .ops
+ * .hw
+ * .parent_names
+ * .num_parents
+ * .flags
+ *
+ * Essentially, everything that would normally be passed into clk_register is
+ * assumed to be initialized already in __clk_init. The other members may be
+ * populated, but are optional.
+ *
+ * __clk_init is only exposed via clk-private.h and is intended for use with
+ * very large numbers of clocks that need to be statically initialized. It is
+ * a layering violation to include clk-private.h from any code which implements
+ * a clock's .ops; as such any statically initialized clock data MUST be in a
+ * separate C file from the logic that implements it's operations.
+ */
+void __clk_init(struct device *dev, struct clk *clk)
+{
+ int i;
+ struct clk *orphan;
+ struct hlist_node *tmp, *tmp2;
+
+ if (!clk)
+ return;
+
+ mutex_lock(&prepare_lock);
+
+ /* check to see if a clock with this name is already registered */
+ if (__clk_lookup(clk->name))
+ goto out;
+
+ /* throw a WARN if any entries in parent_names are NULL */
+ for (i = 0; i < clk->num_parents; i++)
+ WARN(!clk->parent_names[i],
+ "%s: invalid NULL in %s's .parent_names\n",
+ __func__, clk->name);
+
+ /*
+ * Allocate an array of struct clk *'s to avoid unnecessary string
+ * look-ups of clk's possible parents. This can fail for clocks passed
+ * in to clk_init during early boot; thus any access to clk->parents[]
+ * must always check for a NULL pointer and try to populate it if
+ * necessary.
+ *
+ * If clk->parents is not NULL we skip this entire block. This allows
+ * for clock drivers to statically initialize clk->parents.
+ */
+ if (clk->num_parents && !clk->parents) {
+ clk->parents = kmalloc((sizeof(struct clk*) * clk->num_parents),
+ GFP_KERNEL);
+ /*
+ * __clk_lookup returns NULL for parents that have not been
+ * clk_init'd; thus any access to clk->parents[] must check
+ * for a NULL pointer. We can always perform lazy lookups for
+ * missing parents later on.
+ */
+ if (clk->parents)
+ for (i = 0; i < clk->num_parents; i++)
+ clk->parents[i] =
+ __clk_lookup(clk->parent_names[i]);
+ }
+
+ clk->parent = __clk_init_parent(clk);
+
+ /*
+ * Populate clk->parent if parent has already been __clk_init'd. If
+ * parent has not yet been __clk_init'd then place clk in the orphan
+ * list. If clk has set the CLK_IS_ROOT flag then place it in the root
+ * clk list.
+ *
+ * Every time a new clk is clk_init'd then we walk the list of orphan
+ * clocks and re-parent any that are children of the clock currently
+ * being clk_init'd.
+ */
+ if (clk->parent)
+ hlist_add_head(&clk->child_node,
+ &clk->parent->children);
+ else if (clk->flags & CLK_IS_ROOT)
+ hlist_add_head(&clk->child_node, &clk_root_list);
+ else
+ hlist_add_head(&clk->child_node, &clk_orphan_list);
+
+ /*
+ * Set clk's rate. The preferred method is to use .recalc_rate. For
+ * simple clocks and lazy developers the default fallback is to use the
+ * parent's rate. If a clock doesn't have a parent (or is orphaned)
+ * then rate is set to zero.
+ */
+ if (clk->ops->recalc_rate)
+ clk->rate = clk->ops->recalc_rate(clk->hw,
+ __clk_get_rate(clk->parent));
+ else if (clk->parent)
+ clk->rate = clk->parent->rate;
+ else
+ clk->rate = 0;
+
+ /*
+ * walk the list of orphan clocks and reparent any that are children of
+ * this clock
+ */
+ hlist_for_each_entry_safe(orphan, tmp, tmp2, &clk_orphan_list, child_node)
+ for (i = 0; i < orphan->num_parents; i++)
+ if (!strcmp(clk->name, orphan->parent_names[i])) {
+ __clk_reparent(orphan, clk);
+ break;
+ }
+
+ /*
+ * optional platform-specific magic
+ *
+ * The .init callback is not used by any of the basic clock types, but
+ * exists for weird hardware that must perform initialization magic.
+ * Please consider other ways of solving initialization problems before
+ * using this callback, as it's use is discouraged.
+ */
+ if (clk->ops->init)
+ clk->ops->init(clk->hw);
+
+ clk_debug_register(clk);
+
+out:
+ mutex_unlock(&prepare_lock);
+
+ return;
+}
+
+/**
+ * clk_register - allocate a new clock, register it and return an opaque cookie
+ * @dev: device that is registering this clock
+ * @name: clock name
+ * @ops: operations this clock supports
+ * @hw: link to hardware-specific clock data
+ * @parent_names: array of string names for all possible parents
+ * @num_parents: number of possible parents
+ * @flags: framework-level hints and quirks
+ *
+ * clk_register is the primary interface for populating the clock tree with new
+ * clock nodes. It returns a pointer to the newly allocated struct clk which
+ * cannot be dereferenced by driver code but may be used in conjuction with the
+ * rest of the clock API.
+ */
+struct clk *clk_register(struct device *dev, const char *name,
+ const struct clk_ops *ops, struct clk_hw *hw,
+ char **parent_names, u8 num_parents, unsigned long flags)
+{
+ struct clk *clk;
+
+ clk = kzalloc(sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return NULL;
+
+ clk->name = name;
+ clk->ops = ops;
+ clk->hw = hw;
+ clk->flags = flags;
+ clk->parent_names = parent_names;
+ clk->num_parents = num_parents;
+ hw->clk = clk;
+
+ __clk_init(dev, clk);
+
+ return clk;
+}
+EXPORT_SYMBOL_GPL(clk_register);
+
+/*** clk rate change notifiers ***/
+
+/**
+ * clk_notifier_register - add a clk rate change notifier
+ * @clk: struct clk * to watch
+ * @nb: struct notifier_block * with callback info
+ *
+ * Request notification when clk's rate changes. This uses an SRCU
+ * notifier because we want it to block and notifier unregistrations are
+ * uncommon. The callbacks associated with the notifier must not
+ * re-enter into the clk framework by calling any top-level clk APIs;
+ * this will cause a nested prepare_lock mutex.
+ *
+ * Pre-change notifier callbacks will be passed the current, pre-change
+ * rate of the clk via struct clk_notifier_data.old_rate. The new,
+ * post-change rate of the clk is passed via struct
+ * clk_notifier_data.new_rate.
+ *
+ * Post-change notifiers will pass the now-current, post-change rate of
+ * the clk in both struct clk_notifier_data.old_rate and struct
+ * clk_notifier_data.new_rate.
+ *
+ * Abort-change notifiers are effectively the opposite of pre-change
+ * notifiers: the original pre-change clk rate is passed in via struct
+ * clk_notifier_data.new_rate and the failed post-change rate is passed
+ * in via struct clk_notifier_data.old_rate.
+ *
+ * clk_notifier_register() must be called from non-atomic context.
+ * Returns -EINVAL if called with null arguments, -ENOMEM upon
+ * allocation failure; otherwise, passes along the return value of
+ * srcu_notifier_chain_register().
+ */
+int clk_notifier_register(struct clk *clk, struct notifier_block *nb)
+{
+ struct clk_notifier *cn;
+ int ret = -ENOMEM;
+
+ if (!clk || !nb)
+ return -EINVAL;
+
+ mutex_lock(&prepare_lock);
+
+ /* search the list of notifiers for this clk */
+ list_for_each_entry(cn, &clk_notifier_list, node)
+ if (cn->clk == clk)
+ break;
+
+ /* if clk wasn't in the notifier list, allocate new clk_notifier */
+ if (cn->clk != clk) {
+ cn = kzalloc(sizeof(struct clk_notifier), GFP_KERNEL);
+ if (!cn)
+ goto out;
+
+ cn->clk = clk;
+ srcu_init_notifier_head(&cn->notifier_head);
+
+ list_add(&cn->node, &clk_notifier_list);
+ }
+
+ ret = srcu_notifier_chain_register(&cn->notifier_head, nb);
+
+ clk->notifier_count++;
+
+out:
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_notifier_register);
+
+/**
+ * clk_notifier_unregister - remove a clk rate change notifier
+ * @clk: struct clk *
+ * @nb: struct notifier_block * with callback info
+ *
+ * Request no further notification for changes to 'clk' and frees memory
+ * allocated in clk_notifier_register.
+ *
+ * Returns -EINVAL if called with null arguments; otherwise, passes
+ * along the return value of srcu_notifier_chain_unregister().
+ */
+int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
+{
+ struct clk_notifier *cn = NULL;
+ int ret = -EINVAL;
+
+ if (!clk || !nb)
+ return -EINVAL;
+
+ mutex_lock(&prepare_lock);
+
+ list_for_each_entry(cn, &clk_notifier_list, node)
+ if (cn->clk == clk)
+ break;
+
+ if (cn->clk == clk) {
+ ret = srcu_notifier_chain_unregister(&cn->notifier_head, nb);
+
+ clk->notifier_count--;
+
+ /* XXX the notifier code should handle this better */
+ if (!cn->notifier_head.head) {
+ srcu_cleanup_notifier_head(&cn->notifier_head);
+ kfree(cn);
+ }
+
+ } else {
+ ret = -ENOENT;
+ }
+
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_notifier_unregister);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 999d6a0..5138927 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -26,7 +26,6 @@ config CLKSRC_DBX500_PRCMU
config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
bool "Clocksource PRCMU Timer sched_clock"
depends on (CLKSRC_DBX500_PRCMU && !NOMADIK_MTU_SCHED_CLOCK)
- select HAVE_SCHED_CLOCK
default y
help
Use the always on PRCMU Timer as sched_clock
diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c
index f500201..0bf1b89 100644
--- a/drivers/cpufreq/db8500-cpufreq.c
+++ b/drivers/cpufreq/db8500-cpufreq.c
@@ -22,11 +22,11 @@ static struct cpufreq_frequency_table freq_table[] = {
},
[1] = {
.index = 1,
- .frequency = 300000,
+ .frequency = 400000,
},
[2] = {
.index = 2,
- .frequency = 600000,
+ .frequency = 800000,
},
[3] = {
/* Used for MAX_OPP, if available */
@@ -113,12 +113,9 @@ static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table));
- if (!prcmu_is_u8400()) {
- freq_table[1].frequency = 400000;
- freq_table[2].frequency = 800000;
- if (prcmu_has_arm_maxopp())
- freq_table[3].frequency = 1000000;
- }
+ if (prcmu_has_arm_maxopp())
+ freq_table[3].frequency = 1000000;
+
pr_info("db8500-cpufreq : Available frequencies:\n");
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
pr_info(" %d Mhz\n", freq_table[i].frequency/1000);
@@ -145,7 +142,7 @@ static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
policy->cpuinfo.transition_latency = 20 * 1000; /* in ns */
/* policy sharing between dual CPUs */
- cpumask_copy(policy->cpus, &cpu_present_map);
+ cpumask_copy(policy->cpus, cpu_present_mask);
policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index 67bbb06..17fa04d 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -27,7 +27,6 @@
#include <linux/module.h>
#include <linux/regulator/consumer.h>
-#include <asm/system.h>
#include <asm/smp_plat.h>
#include <asm/cpu.h>
diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c
index cf7e1ee..334cc2f 100644
--- a/drivers/cpufreq/powernow-k7.c
+++ b/drivers/cpufreq/powernow-k7.c
@@ -27,7 +27,6 @@
#include <asm/timer.h> /* Needed for recalibrate_cpu_khz() */
#include <asm/msr.h>
-#include <asm/system.h>
#include <asm/cpu_device_id.h>
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 6588f43..87411ce 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -53,6 +53,52 @@ static void cpuidle_kick_cpus(void) {}
static int __cpuidle_register_device(struct cpuidle_device *dev);
+static inline int cpuidle_enter(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
+{
+ struct cpuidle_state *target_state = &drv->states[index];
+ return target_state->enter(dev, drv, index);
+}
+
+static inline int cpuidle_enter_tk(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
+{
+ return cpuidle_wrap_enter(dev, drv, index, cpuidle_enter);
+}
+
+typedef int (*cpuidle_enter_t)(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index);
+
+static cpuidle_enter_t cpuidle_enter_ops;
+
+/**
+ * cpuidle_play_dead - cpu off-lining
+ *
+ * Only returns in case of an error
+ */
+int cpuidle_play_dead(void)
+{
+ struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
+ struct cpuidle_driver *drv = cpuidle_get_driver();
+ int i, dead_state = -1;
+ int power_usage = -1;
+
+ /* Find lowest-power state that supports long-term idle */
+ for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
+ struct cpuidle_state *s = &drv->states[i];
+
+ if (s->power_usage < power_usage && s->enter_dead) {
+ power_usage = s->power_usage;
+ dead_state = i;
+ }
+ }
+
+ if (dead_state != -1)
+ return drv->states[dead_state].enter_dead(dev, dead_state);
+
+ return -ENODEV;
+}
+
/**
* cpuidle_idle_call - the main idle loop
*
@@ -63,7 +109,6 @@ int cpuidle_idle_call(void)
{
struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
struct cpuidle_driver *drv = cpuidle_get_driver();
- struct cpuidle_state *target_state;
int next_state, entered_state;
if (off)
@@ -92,12 +137,10 @@ int cpuidle_idle_call(void)
return 0;
}
- target_state = &drv->states[next_state];
-
trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu);
trace_cpu_idle_rcuidle(next_state, dev->cpu);
- entered_state = target_state->enter(dev, drv, next_state);
+ entered_state = cpuidle_enter_ops(dev, drv, next_state);
trace_power_end_rcuidle(dev->cpu);
trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
@@ -110,6 +153,8 @@ int cpuidle_idle_call(void)
dev->states_usage[entered_state].time +=
(unsigned long long)dev->last_residency;
dev->states_usage[entered_state].usage++;
+ } else {
+ dev->last_residency = 0;
}
/* give the governor an opportunity to reflect on the outcome */
@@ -164,6 +209,37 @@ void cpuidle_resume_and_unlock(void)
EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock);
+/**
+ * cpuidle_wrap_enter - performs timekeeping and irqen around enter function
+ * @dev: pointer to a valid cpuidle_device object
+ * @drv: pointer to a valid cpuidle_driver object
+ * @index: index of the target cpuidle state.
+ */
+int cpuidle_wrap_enter(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index,
+ int (*enter)(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index))
+{
+ ktime_t time_start, time_end;
+ s64 diff;
+
+ time_start = ktime_get();
+
+ index = enter(dev, drv, index);
+
+ time_end = ktime_get();
+
+ local_irq_enable();
+
+ diff = ktime_to_us(ktime_sub(time_end, time_start));
+ if (diff > INT_MAX)
+ diff = INT_MAX;
+
+ dev->last_residency = (int) diff;
+
+ return index;
+}
+
#ifdef CONFIG_ARCH_HAS_CPU_RELAX
static int poll_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
@@ -197,6 +273,7 @@ static void poll_idle_init(struct cpuidle_driver *drv)
state->power_usage = -1;
state->flags = 0;
state->enter = poll_idle;
+ state->disable = 0;
}
#else
static void poll_idle_init(struct cpuidle_driver *drv) {}
@@ -212,13 +289,14 @@ static void poll_idle_init(struct cpuidle_driver *drv) {}
int cpuidle_enable_device(struct cpuidle_device *dev)
{
int ret, i;
+ struct cpuidle_driver *drv = cpuidle_get_driver();
if (dev->enabled)
return 0;
- if (!cpuidle_get_driver() || !cpuidle_curr_governor)
+ if (!drv || !cpuidle_curr_governor)
return -EIO;
if (!dev->state_count)
- return -EINVAL;
+ dev->state_count = drv->state_count;
if (dev->registered == 0) {
ret = __cpuidle_register_device(dev);
@@ -226,13 +304,16 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
return ret;
}
- poll_idle_init(cpuidle_get_driver());
+ cpuidle_enter_ops = drv->en_core_tk_irqen ?
+ cpuidle_enter_tk : cpuidle_enter;
+
+ poll_idle_init(drv);
if ((ret = cpuidle_add_state_sysfs(dev)))
return ret;
if (cpuidle_curr_governor->enable &&
- (ret = cpuidle_curr_governor->enable(cpuidle_get_driver(), dev)))
+ (ret = cpuidle_curr_governor->enable(drv, dev)))
goto fail_sysfs;
for (i = 0; i < dev->state_count; i++) {
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index 284d7af..40cd3f3 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -47,7 +47,7 @@ static void __cpuidle_register_driver(struct cpuidle_driver *drv)
*/
int cpuidle_register_driver(struct cpuidle_driver *drv)
{
- if (!drv)
+ if (!drv || !drv->state_count)
return -EINVAL;
if (cpuidle_disabled())
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index ad09526..0633575 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -236,7 +236,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
struct menu_device *data = &__get_cpu_var(menu_devices);
int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
- unsigned int power_usage = -1;
+ int power_usage = -1;
int i;
int multiplier;
struct timespec t;
@@ -280,7 +280,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
* We want to default to C1 (hlt), not to busy polling
* unless the timer is happening really really soon.
*/
- if (data->expected_us > 5)
+ if (data->expected_us > 5 &&
+ drv->states[CPUIDLE_DRIVER_STATE_START].disable == 0)
data->last_state_idx = CPUIDLE_DRIVER_STATE_START;
/*
@@ -290,6 +291,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
struct cpuidle_state *s = &drv->states[i];
+ if (s->disable)
+ continue;
if (s->target_residency > data->predicted_us)
continue;
if (s->exit_latency > latency_req)
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
index 3fe41fe..88032b4 100644
--- a/drivers/cpuidle/sysfs.c
+++ b/drivers/cpuidle/sysfs.c
@@ -11,6 +11,7 @@
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/cpu.h>
+#include <linux/capability.h>
#include "cpuidle.h"
@@ -222,6 +223,9 @@ struct cpuidle_state_attr {
#define define_one_state_ro(_name, show) \
static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL)
+#define define_one_state_rw(_name, show, store) \
+static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0644, show, store)
+
#define define_show_state_function(_name) \
static ssize_t show_state_##_name(struct cpuidle_state *state, \
struct cpuidle_state_usage *state_usage, char *buf) \
@@ -229,6 +233,24 @@ static ssize_t show_state_##_name(struct cpuidle_state *state, \
return sprintf(buf, "%u\n", state->_name);\
}
+#define define_store_state_function(_name) \
+static ssize_t store_state_##_name(struct cpuidle_state *state, \
+ const char *buf, size_t size) \
+{ \
+ long value; \
+ int err; \
+ if (!capable(CAP_SYS_ADMIN)) \
+ return -EPERM; \
+ err = kstrtol(buf, 0, &value); \
+ if (err) \
+ return err; \
+ if (value) \
+ state->disable = 1; \
+ else \
+ state->disable = 0; \
+ return size; \
+}
+
#define define_show_state_ull_function(_name) \
static ssize_t show_state_##_name(struct cpuidle_state *state, \
struct cpuidle_state_usage *state_usage, char *buf) \
@@ -251,6 +273,8 @@ define_show_state_ull_function(usage)
define_show_state_ull_function(time)
define_show_state_str_function(name)
define_show_state_str_function(desc)
+define_show_state_function(disable)
+define_store_state_function(disable)
define_one_state_ro(name, show_state_name);
define_one_state_ro(desc, show_state_desc);
@@ -258,6 +282,7 @@ define_one_state_ro(latency, show_state_exit_latency);
define_one_state_ro(power, show_state_power_usage);
define_one_state_ro(usage, show_state_usage);
define_one_state_ro(time, show_state_time);
+define_one_state_rw(disable, show_state_disable, store_state_disable);
static struct attribute *cpuidle_state_default_attrs[] = {
&attr_name.attr,
@@ -266,6 +291,7 @@ static struct attribute *cpuidle_state_default_attrs[] = {
&attr_power.attr,
&attr_usage.attr,
&attr_time.attr,
+ &attr_disable.attr,
NULL
};
@@ -287,8 +313,22 @@ static ssize_t cpuidle_state_show(struct kobject * kobj,
return ret;
}
+static ssize_t cpuidle_state_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t size)
+{
+ int ret = -EIO;
+ struct cpuidle_state *state = kobj_to_state(kobj);
+ struct cpuidle_state_attr *cattr = attr_to_stateattr(attr);
+
+ if (cattr->store)
+ ret = cattr->store(state, buf, size);
+
+ return ret;
+}
+
static const struct sysfs_ops cpuidle_state_sysfs_ops = {
.show = cpuidle_state_show,
+ .store = cpuidle_state_store,
};
static void cpuidle_state_sysfs_release(struct kobject *kobj)
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index f1a2749..cf9da36 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -201,7 +201,6 @@ config PL330_DMA
tristate "DMA API Driver for PL330"
select DMA_ENGINE
depends on ARM_AMBA
- select PL330
help
Select if your platform has one or more PL330 DMACs.
You need to provide platform specific settings via
@@ -231,7 +230,7 @@ config IMX_SDMA
config IMX_DMA
tristate "i.MX DMA support"
- depends on IMX_HAVE_DMA_V1
+ depends on ARCH_MXC
select DMA_ENGINE
help
Support the i.MX DMA engine. This engine is integrated into
@@ -252,6 +251,15 @@ config EP93XX_DMA
help
Enable support for the Cirrus Logic EP93xx M2P/M2M DMA controller.
+config DMA_SA11X0
+ tristate "SA-11x0 DMA support"
+ depends on ARCH_SA1100
+ select DMA_ENGINE
+ help
+ Support the DMA engine found on Intel StrongARM SA-1100 and
+ SA-1110 SoCs. This DMA engine can only be used with on-chip
+ devices.
+
config DMA_ENGINE
bool
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 009a222..86b795b 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -27,3 +27,4 @@ obj-$(CONFIG_PL330_DMA) += pl330.o
obj-$(CONFIG_PCH_DMA) += pch_dma.o
obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
+obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index 8a28158..c301a8e 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -85,6 +85,8 @@
#include <linux/slab.h>
#include <asm/hardware/pl080.h>
+#include "dmaengine.h"
+
#define DRIVER_NAME "pl08xdmac"
static struct amba_driver pl08x_amba_driver;
@@ -649,7 +651,7 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
}
if ((bd.srcbus.addr % bd.srcbus.buswidth) ||
- (bd.srcbus.addr % bd.srcbus.buswidth)) {
+ (bd.dstbus.addr % bd.dstbus.buswidth)) {
dev_err(&pl08x->adev->dev,
"%s src & dst address must be aligned to src"
" & dst width if peripheral is flow controller",
@@ -919,13 +921,10 @@ static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx)
struct pl08x_dma_chan *plchan = to_pl08x_chan(tx->chan);
struct pl08x_txd *txd = to_pl08x_txd(tx);
unsigned long flags;
+ dma_cookie_t cookie;
spin_lock_irqsave(&plchan->lock, flags);
-
- plchan->chan.cookie += 1;
- if (plchan->chan.cookie < 0)
- plchan->chan.cookie = 1;
- tx->cookie = plchan->chan.cookie;
+ cookie = dma_cookie_assign(tx);
/* Put this onto the pending list */
list_add_tail(&txd->node, &plchan->pend_list);
@@ -945,7 +944,7 @@ static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx)
spin_unlock_irqrestore(&plchan->lock, flags);
- return tx->cookie;
+ return cookie;
}
static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt(
@@ -965,31 +964,17 @@ static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *txstate)
{
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
enum dma_status ret;
- u32 bytesleft = 0;
- last_used = plchan->chan.cookie;
- last_complete = plchan->lc;
-
- ret = dma_async_is_complete(cookie, last_complete, last_used);
- if (ret == DMA_SUCCESS) {
- dma_set_tx_state(txstate, last_complete, last_used, 0);
+ ret = dma_cookie_status(chan, cookie, txstate);
+ if (ret == DMA_SUCCESS)
return ret;
- }
/*
* This cookie not complete yet
+ * Get number of bytes left in the active transactions and queue
*/
- last_used = plchan->chan.cookie;
- last_complete = plchan->lc;
-
- /* Get number of bytes left in the active transactions and queue */
- bytesleft = pl08x_getbytes_chan(plchan);
-
- dma_set_tx_state(txstate, last_complete, last_used,
- bytesleft);
+ dma_set_residue(txstate, pl08x_getbytes_chan(plchan));
if (plchan->state == PL08X_CHAN_PAUSED)
return DMA_PAUSED;
@@ -1139,6 +1124,8 @@ static int dma_set_runtime_config(struct dma_chan *chan,
cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT;
cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT;
+ plchan->device_fc = config->device_fc;
+
if (plchan->runtime_direction == DMA_DEV_TO_MEM) {
plchan->src_addr = config->src_addr;
plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR |
@@ -1326,7 +1313,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
- unsigned long flags)
+ unsigned long flags, void *context)
{
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
struct pl08x_driver_data *pl08x = plchan->host;
@@ -1370,7 +1357,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
return NULL;
}
- if (plchan->cd->device_fc)
+ if (plchan->device_fc)
tmp = (direction == DMA_MEM_TO_DEV) ? PL080_FLOW_MEM2PER_PER :
PL080_FLOW_PER2MEM_PER;
else
@@ -1541,7 +1528,7 @@ static void pl08x_tasklet(unsigned long data)
if (txd) {
/* Update last completed */
- plchan->lc = txd->tx.cookie;
+ dma_cookie_complete(&txd->tx);
}
/* If a new descriptor is queued, set it up plchan->at is NULL here */
@@ -1722,8 +1709,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
chan->name);
chan->chan.device = dmadev;
- chan->chan.cookie = 0;
- chan->lc = 0;
+ dma_cookie_init(&chan->chan);
spin_lock_init(&chan->lock);
INIT_LIST_HEAD(&chan->pend_list);
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index f4aed5f..7aa58d2 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -27,6 +27,7 @@
#include <linux/of_device.h>
#include "at_hdmac_regs.h"
+#include "dmaengine.h"
/*
* Glossary
@@ -192,27 +193,6 @@ static void atc_desc_chain(struct at_desc **first, struct at_desc **prev,
}
/**
- * atc_assign_cookie - compute and assign new cookie
- * @atchan: channel we work on
- * @desc: descriptor to assign cookie for
- *
- * Called with atchan->lock held and bh disabled
- */
-static dma_cookie_t
-atc_assign_cookie(struct at_dma_chan *atchan, struct at_desc *desc)
-{
- dma_cookie_t cookie = atchan->chan_common.cookie;
-
- if (++cookie < 0)
- cookie = 1;
-
- atchan->chan_common.cookie = cookie;
- desc->txd.cookie = cookie;
-
- return cookie;
-}
-
-/**
* atc_dostart - starts the DMA engine for real
* @atchan: the channel we want to start
* @first: first descriptor in the list we want to begin with
@@ -269,7 +249,7 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc)
dev_vdbg(chan2dev(&atchan->chan_common),
"descriptor %u complete\n", txd->cookie);
- atchan->completed_cookie = txd->cookie;
+ dma_cookie_complete(txd);
/* move children to free_list */
list_splice_init(&desc->tx_list, &atchan->free_list);
@@ -547,7 +527,7 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx)
unsigned long flags;
spin_lock_irqsave(&atchan->lock, flags);
- cookie = atc_assign_cookie(atchan, desc);
+ cookie = dma_cookie_assign(tx);
if (list_empty(&atchan->active_list)) {
dev_vdbg(chan2dev(tx->chan), "tx_submit: started %u\n",
@@ -659,14 +639,16 @@ err_desc_get:
* @sg_len: number of entries in @scatterlist
* @direction: DMA direction
* @flags: tx descriptor status flags
+ * @context: transaction context (ignored)
*/
static struct dma_async_tx_descriptor *
atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
- unsigned long flags)
+ unsigned long flags, void *context)
{
struct at_dma_chan *atchan = to_at_dma_chan(chan);
struct at_dma_slave *atslave = chan->private;
+ struct dma_slave_config *sconfig = &atchan->dma_sconfig;
struct at_desc *first = NULL;
struct at_desc *prev = NULL;
u32 ctrla;
@@ -688,19 +670,18 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
return NULL;
}
- reg_width = atslave->reg_width;
-
ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla;
ctrlb = ATC_IEN;
switch (direction) {
case DMA_MEM_TO_DEV:
+ reg_width = convert_buswidth(sconfig->dst_addr_width);
ctrla |= ATC_DST_WIDTH(reg_width);
ctrlb |= ATC_DST_ADDR_MODE_FIXED
| ATC_SRC_ADDR_MODE_INCR
| ATC_FC_MEM2PER
| ATC_SIF(AT_DMA_MEM_IF) | ATC_DIF(AT_DMA_PER_IF);
- reg = atslave->tx_reg;
+ reg = sconfig->dst_addr;
for_each_sg(sgl, sg, sg_len, i) {
struct at_desc *desc;
u32 len;
@@ -728,13 +709,14 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
}
break;
case DMA_DEV_TO_MEM:
+ reg_width = convert_buswidth(sconfig->src_addr_width);
ctrla |= ATC_SRC_WIDTH(reg_width);
ctrlb |= ATC_DST_ADDR_MODE_INCR
| ATC_SRC_ADDR_MODE_FIXED
| ATC_FC_PER2MEM
| ATC_SIF(AT_DMA_PER_IF) | ATC_DIF(AT_DMA_MEM_IF);
- reg = atslave->rx_reg;
+ reg = sconfig->src_addr;
for_each_sg(sgl, sg, sg_len, i) {
struct at_desc *desc;
u32 len;
@@ -810,12 +792,15 @@ err_out:
* atc_dma_cyclic_fill_desc - Fill one period decriptor
*/
static int
-atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc,
+atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc,
unsigned int period_index, dma_addr_t buf_addr,
- size_t period_len, enum dma_transfer_direction direction)
+ unsigned int reg_width, size_t period_len,
+ enum dma_transfer_direction direction)
{
- u32 ctrla;
- unsigned int reg_width = atslave->reg_width;
+ struct at_dma_chan *atchan = to_at_dma_chan(chan);
+ struct at_dma_slave *atslave = chan->private;
+ struct dma_slave_config *sconfig = &atchan->dma_sconfig;
+ u32 ctrla;
/* prepare common CRTLA value */
ctrla = ATC_DEFAULT_CTRLA | atslave->ctrla
@@ -826,7 +811,7 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc,
switch (direction) {
case DMA_MEM_TO_DEV:
desc->lli.saddr = buf_addr + (period_len * period_index);
- desc->lli.daddr = atslave->tx_reg;
+ desc->lli.daddr = sconfig->dst_addr;
desc->lli.ctrla = ctrla;
desc->lli.ctrlb = ATC_DST_ADDR_MODE_FIXED
| ATC_SRC_ADDR_MODE_INCR
@@ -836,7 +821,7 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc,
break;
case DMA_DEV_TO_MEM:
- desc->lli.saddr = atslave->rx_reg;
+ desc->lli.saddr = sconfig->src_addr;
desc->lli.daddr = buf_addr + (period_len * period_index);
desc->lli.ctrla = ctrla;
desc->lli.ctrlb = ATC_DST_ADDR_MODE_INCR
@@ -860,16 +845,20 @@ atc_dma_cyclic_fill_desc(struct at_dma_slave *atslave, struct at_desc *desc,
* @buf_len: total number of bytes for the entire buffer
* @period_len: number of bytes for each period
* @direction: transfer direction, to or from device
+ * @context: transfer context (ignored)
*/
static struct dma_async_tx_descriptor *
atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
- size_t period_len, enum dma_transfer_direction direction)
+ size_t period_len, enum dma_transfer_direction direction,
+ void *context)
{
struct at_dma_chan *atchan = to_at_dma_chan(chan);
struct at_dma_slave *atslave = chan->private;
+ struct dma_slave_config *sconfig = &atchan->dma_sconfig;
struct at_desc *first = NULL;
struct at_desc *prev = NULL;
unsigned long was_cyclic;
+ unsigned int reg_width;
unsigned int periods = buf_len / period_len;
unsigned int i;
@@ -889,8 +878,13 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
return NULL;
}
+ if (sconfig->direction == DMA_MEM_TO_DEV)
+ reg_width = convert_buswidth(sconfig->dst_addr_width);
+ else
+ reg_width = convert_buswidth(sconfig->src_addr_width);
+
/* Check for too big/unaligned periods and unaligned DMA buffer */
- if (atc_dma_cyclic_check_values(atslave->reg_width, buf_addr,
+ if (atc_dma_cyclic_check_values(reg_width, buf_addr,
period_len, direction))
goto err_out;
@@ -902,8 +896,8 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
if (!desc)
goto err_desc_get;
- if (atc_dma_cyclic_fill_desc(atslave, desc, i, buf_addr,
- period_len, direction))
+ if (atc_dma_cyclic_fill_desc(chan, desc, i, buf_addr,
+ reg_width, period_len, direction))
goto err_desc_get;
atc_desc_chain(&first, &prev, desc);
@@ -926,6 +920,23 @@ err_out:
return NULL;
}
+static int set_runtime_config(struct dma_chan *chan,
+ struct dma_slave_config *sconfig)
+{
+ struct at_dma_chan *atchan = to_at_dma_chan(chan);
+
+ /* Check if it is chan is configured for slave transfers */
+ if (!chan->private)
+ return -EINVAL;
+
+ memcpy(&atchan->dma_sconfig, sconfig, sizeof(*sconfig));
+
+ convert_burst(&atchan->dma_sconfig.src_maxburst);
+ convert_burst(&atchan->dma_sconfig.dst_maxburst);
+
+ return 0;
+}
+
static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
unsigned long arg)
@@ -986,6 +997,8 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
clear_bit(ATC_IS_CYCLIC, &atchan->status);
spin_unlock_irqrestore(&atchan->lock, flags);
+ } else if (cmd == DMA_SLAVE_CONFIG) {
+ return set_runtime_config(chan, (struct dma_slave_config *)arg);
} else {
return -ENXIO;
}
@@ -1016,26 +1029,20 @@ atc_tx_status(struct dma_chan *chan,
spin_lock_irqsave(&atchan->lock, flags);
- last_complete = atchan->completed_cookie;
- last_used = chan->cookie;
-
- ret = dma_async_is_complete(cookie, last_complete, last_used);
+ ret = dma_cookie_status(chan, cookie, txstate);
if (ret != DMA_SUCCESS) {
atc_cleanup_descriptors(atchan);
- last_complete = atchan->completed_cookie;
- last_used = chan->cookie;
-
- ret = dma_async_is_complete(cookie, last_complete, last_used);
+ ret = dma_cookie_status(chan, cookie, txstate);
}
+ last_complete = chan->completed_cookie;
+ last_used = chan->cookie;
+
spin_unlock_irqrestore(&atchan->lock, flags);
if (ret != DMA_SUCCESS)
- dma_set_tx_state(txstate, last_complete, last_used,
- atc_first_active(atchan)->len);
- else
- dma_set_tx_state(txstate, last_complete, last_used, 0);
+ dma_set_residue(txstate, atc_first_active(atchan)->len);
if (atc_chan_is_paused(atchan))
ret = DMA_PAUSED;
@@ -1129,7 +1136,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan)
spin_lock_irqsave(&atchan->lock, flags);
atchan->descs_allocated = i;
list_splice(&tmp_list, &atchan->free_list);
- atchan->completed_cookie = chan->cookie = 1;
+ dma_cookie_init(chan);
spin_unlock_irqrestore(&atchan->lock, flags);
/* channel parameters */
@@ -1329,7 +1336,7 @@ static int __init at_dma_probe(struct platform_device *pdev)
struct at_dma_chan *atchan = &atdma->chan[i];
atchan->chan_common.device = &atdma->dma_common;
- atchan->chan_common.cookie = atchan->completed_cookie = 1;
+ dma_cookie_init(&atchan->chan_common);
list_add_tail(&atchan->chan_common.device_node,
&atdma->dma_common.channels);
diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h
index a8d3277..897a8bc 100644
--- a/drivers/dma/at_hdmac_regs.h
+++ b/drivers/dma/at_hdmac_regs.h
@@ -207,8 +207,8 @@ enum atc_status {
* @save_cfg: configuration register that is saved on suspend/resume cycle
* @save_dscr: for cyclic operations, preserve next descriptor address in
* the cyclic list on suspend/resume cycle
+ * @dma_sconfig: configuration for slave transfers, passed via DMA_SLAVE_CONFIG
* @lock: serializes enqueue/dequeue operations to descriptors lists
- * @completed_cookie: identifier for the most recently completed operation
* @active_list: list of descriptors dmaengine is being running on
* @queue: list of descriptors ready to be submitted to engine
* @free_list: list of descriptors usable by the channel
@@ -223,11 +223,11 @@ struct at_dma_chan {
struct tasklet_struct tasklet;
u32 save_cfg;
u32 save_dscr;
+ struct dma_slave_config dma_sconfig;
spinlock_t lock;
/* these other elements are all protected by lock */
- dma_cookie_t completed_cookie;
struct list_head active_list;
struct list_head queue;
struct list_head free_list;
@@ -245,6 +245,36 @@ static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan)
return container_of(dchan, struct at_dma_chan, chan_common);
}
+/*
+ * Fix sconfig's burst size according to at_hdmac. We need to convert them as:
+ * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3, 32 -> 4, 64 -> 5, 128 -> 6, 256 -> 7.
+ *
+ * This can be done by finding most significant bit set.
+ */
+static inline void convert_burst(u32 *maxburst)
+{
+ if (*maxburst > 1)
+ *maxburst = fls(*maxburst) - 2;
+ else
+ *maxburst = 0;
+}
+
+/*
+ * Fix sconfig's bus width according to at_hdmac.
+ * 1 byte -> 0, 2 bytes -> 1, 4 bytes -> 2.
+ */
+static inline u8 convert_buswidth(enum dma_slave_buswidth addr_width)
+{
+ switch (addr_width) {
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ return 1;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ return 2;
+ default:
+ /* For 1 byte width or fallback */
+ return 0;
+ }
+}
/*-- Controller ------------------------------------------------------*/
diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c
index d65a718..dc89455 100644
--- a/drivers/dma/coh901318.c
+++ b/drivers/dma/coh901318.c
@@ -24,6 +24,7 @@
#include <mach/coh901318.h>
#include "coh901318_lli.h"
+#include "dmaengine.h"
#define COHC_2_DEV(cohc) (&cohc->chan.dev->device)
@@ -59,7 +60,6 @@ struct coh901318_base {
struct coh901318_chan {
spinlock_t lock;
int allocated;
- int completed;
int id;
int stopped;
@@ -318,20 +318,6 @@ static int coh901318_prep_linked_list(struct coh901318_chan *cohc,
return 0;
}
-static dma_cookie_t
-coh901318_assign_cookie(struct coh901318_chan *cohc,
- struct coh901318_desc *cohd)
-{
- dma_cookie_t cookie = cohc->chan.cookie;
-
- if (++cookie < 0)
- cookie = 1;
-
- cohc->chan.cookie = cookie;
- cohd->desc.cookie = cookie;
-
- return cookie;
-}
static struct coh901318_desc *
coh901318_desc_get(struct coh901318_chan *cohc)
@@ -705,7 +691,7 @@ static void dma_tasklet(unsigned long data)
callback_param = cohd_fin->desc.callback_param;
/* sign this job as completed on the channel */
- cohc->completed = cohd_fin->desc.cookie;
+ dma_cookie_complete(&cohd_fin->desc);
/* release the lli allocation and remove the descriptor */
coh901318_lli_free(&cohc->base->pool, &cohd_fin->lli);
@@ -929,7 +915,7 @@ static int coh901318_alloc_chan_resources(struct dma_chan *chan)
coh901318_config(cohc, NULL);
cohc->allocated = 1;
- cohc->completed = chan->cookie = 1;
+ dma_cookie_init(chan);
spin_unlock_irqrestore(&cohc->lock, flags);
@@ -966,16 +952,16 @@ coh901318_tx_submit(struct dma_async_tx_descriptor *tx)
desc);
struct coh901318_chan *cohc = to_coh901318_chan(tx->chan);
unsigned long flags;
+ dma_cookie_t cookie;
spin_lock_irqsave(&cohc->lock, flags);
-
- tx->cookie = coh901318_assign_cookie(cohc, cohd);
+ cookie = dma_cookie_assign(tx);
coh901318_desc_queue(cohc, cohd);
spin_unlock_irqrestore(&cohc->lock, flags);
- return tx->cookie;
+ return cookie;
}
static struct dma_async_tx_descriptor *
@@ -1035,7 +1021,7 @@ coh901318_prep_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
static struct dma_async_tx_descriptor *
coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
- unsigned long flags)
+ unsigned long flags, void *context)
{
struct coh901318_chan *cohc = to_coh901318_chan(chan);
struct coh901318_lli *lli;
@@ -1165,17 +1151,12 @@ coh901318_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
struct coh901318_chan *cohc = to_coh901318_chan(chan);
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
- int ret;
-
- last_complete = cohc->completed;
- last_used = chan->cookie;
+ enum dma_status ret;
- ret = dma_async_is_complete(cookie, last_complete, last_used);
+ ret = dma_cookie_status(chan, cookie, txstate);
+ /* FIXME: should be conditional on ret != DMA_SUCCESS? */
+ dma_set_residue(txstate, coh901318_get_bytes_left(chan));
- dma_set_tx_state(txstate, last_complete, last_used,
- coh901318_get_bytes_left(chan));
if (ret == DMA_IN_PROGRESS && cohc->stopped)
ret = DMA_PAUSED;
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index a6c6051..767bcc3 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -510,8 +510,8 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v
dma_chan_name(chan));
list_del_rcu(&device->global_node);
} else if (err)
- pr_debug("dmaengine: failed to get %s: (%d)\n",
- dma_chan_name(chan), err);
+ pr_debug("%s: failed to get %s: (%d)\n",
+ __func__, dma_chan_name(chan), err);
else
break;
if (--device->privatecnt == 0)
@@ -564,8 +564,8 @@ void dmaengine_get(void)
list_del_rcu(&device->global_node);
break;
} else if (err)
- pr_err("dmaengine: failed to get %s: (%d)\n",
- dma_chan_name(chan), err);
+ pr_err("%s: failed to get %s: (%d)\n",
+ __func__, dma_chan_name(chan), err);
}
}
diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h
new file mode 100644
index 0000000..17f983a
--- /dev/null
+++ b/drivers/dma/dmaengine.h
@@ -0,0 +1,89 @@
+/*
+ * The contents of this file are private to DMA engine drivers, and is not
+ * part of the API to be used by DMA engine users.
+ */
+#ifndef DMAENGINE_H
+#define DMAENGINE_H
+
+#include <linux/bug.h>
+#include <linux/dmaengine.h>
+
+/**
+ * dma_cookie_init - initialize the cookies for a DMA channel
+ * @chan: dma channel to initialize
+ */
+static inline void dma_cookie_init(struct dma_chan *chan)
+{
+ chan->cookie = DMA_MIN_COOKIE;
+ chan->completed_cookie = DMA_MIN_COOKIE;
+}
+
+/**
+ * dma_cookie_assign - assign a DMA engine cookie to the descriptor
+ * @tx: descriptor needing cookie
+ *
+ * Assign a unique non-zero per-channel cookie to the descriptor.
+ * Note: caller is expected to hold a lock to prevent concurrency.
+ */
+static inline dma_cookie_t dma_cookie_assign(struct dma_async_tx_descriptor *tx)
+{
+ struct dma_chan *chan = tx->chan;
+ dma_cookie_t cookie;
+
+ cookie = chan->cookie + 1;
+ if (cookie < DMA_MIN_COOKIE)
+ cookie = DMA_MIN_COOKIE;
+ tx->cookie = chan->cookie = cookie;
+
+ return cookie;
+}
+
+/**
+ * dma_cookie_complete - complete a descriptor
+ * @tx: descriptor to complete
+ *
+ * Mark this descriptor complete by updating the channels completed
+ * cookie marker. Zero the descriptors cookie to prevent accidental
+ * repeated completions.
+ *
+ * Note: caller is expected to hold a lock to prevent concurrency.
+ */
+static inline void dma_cookie_complete(struct dma_async_tx_descriptor *tx)
+{
+ BUG_ON(tx->cookie < DMA_MIN_COOKIE);
+ tx->chan->completed_cookie = tx->cookie;
+ tx->cookie = 0;
+}
+
+/**
+ * dma_cookie_status - report cookie status
+ * @chan: dma channel
+ * @cookie: cookie we are interested in
+ * @state: dma_tx_state structure to return last/used cookies
+ *
+ * Report the status of the cookie, filling in the state structure if
+ * non-NULL. No locking is required.
+ */
+static inline enum dma_status dma_cookie_status(struct dma_chan *chan,
+ dma_cookie_t cookie, struct dma_tx_state *state)
+{
+ dma_cookie_t used, complete;
+
+ used = chan->cookie;
+ complete = chan->completed_cookie;
+ barrier();
+ if (state) {
+ state->last = complete;
+ state->used = used;
+ state->residue = 0;
+ }
+ return dma_async_is_complete(cookie, complete, used);
+}
+
+static inline void dma_set_residue(struct dma_tx_state *state, u32 residue)
+{
+ if (state)
+ state->residue = residue;
+}
+
+#endif
diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c
index 9b592b0..7439079 100644
--- a/drivers/dma/dw_dmac.c
+++ b/drivers/dma/dw_dmac.c
@@ -9,6 +9,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
@@ -22,6 +23,7 @@
#include <linux/slab.h>
#include "dw_dmac_regs.h"
+#include "dmaengine.h"
/*
* This supports the Synopsys "DesignWare AHB Central DMA Controller",
@@ -33,19 +35,23 @@
* which does not support descriptor writeback.
*/
-#define DWC_DEFAULT_CTLLO(private) ({ \
- struct dw_dma_slave *__slave = (private); \
- int dms = __slave ? __slave->dst_master : 0; \
- int sms = __slave ? __slave->src_master : 1; \
- u8 smsize = __slave ? __slave->src_msize : DW_DMA_MSIZE_16; \
- u8 dmsize = __slave ? __slave->dst_msize : DW_DMA_MSIZE_16; \
+#define DWC_DEFAULT_CTLLO(_chan) ({ \
+ struct dw_dma_slave *__slave = (_chan->private); \
+ struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \
+ struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \
+ int _dms = __slave ? __slave->dst_master : 0; \
+ int _sms = __slave ? __slave->src_master : 1; \
+ u8 _smsize = __slave ? _sconfig->src_maxburst : \
+ DW_DMA_MSIZE_16; \
+ u8 _dmsize = __slave ? _sconfig->dst_maxburst : \
+ DW_DMA_MSIZE_16; \
\
- (DWC_CTLL_DST_MSIZE(dmsize) \
- | DWC_CTLL_SRC_MSIZE(smsize) \
+ (DWC_CTLL_DST_MSIZE(_dmsize) \
+ | DWC_CTLL_SRC_MSIZE(_smsize) \
| DWC_CTLL_LLP_D_EN \
| DWC_CTLL_LLP_S_EN \
- | DWC_CTLL_DMS(dms) \
- | DWC_CTLL_SMS(sms)); \
+ | DWC_CTLL_DMS(_dms) \
+ | DWC_CTLL_SMS(_sms)); \
})
/*
@@ -151,21 +157,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc)
}
}
-/* Called with dwc->lock held and bh disabled */
-static dma_cookie_t
-dwc_assign_cookie(struct dw_dma_chan *dwc, struct dw_desc *desc)
-{
- dma_cookie_t cookie = dwc->chan.cookie;
-
- if (++cookie < 0)
- cookie = 1;
-
- dwc->chan.cookie = cookie;
- desc->txd.cookie = cookie;
-
- return cookie;
-}
-
static void dwc_initialize(struct dw_dma_chan *dwc)
{
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
@@ -192,7 +183,6 @@ static void dwc_initialize(struct dw_dma_chan *dwc)
/* Enable interrupts */
channel_set_bit(dw, MASK.XFER, dwc->mask);
- channel_set_bit(dw, MASK.BLOCK, dwc->mask);
channel_set_bit(dw, MASK.ERROR, dwc->mask);
dwc->initialized = true;
@@ -245,7 +235,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc,
dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie);
spin_lock_irqsave(&dwc->lock, flags);
- dwc->completed = txd->cookie;
+ dma_cookie_complete(txd);
if (callback_required) {
callback = txd->callback;
param = txd->callback_param;
@@ -329,12 +319,6 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc)
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
- /*
- * Clear block interrupt flag before scanning so that we don't
- * miss any, and read LLP before RAW_XFER to ensure it is
- * valid if we decide to scan the list.
- */
- dma_writel(dw, CLEAR.BLOCK, dwc->mask);
llp = channel_readl(dwc, LLP);
status_xfer = dma_readl(dw, RAW.XFER);
@@ -470,17 +454,16 @@ EXPORT_SYMBOL(dw_dma_get_dst_addr);
/* called with dwc->lock held and all DMAC interrupts disabled */
static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc,
- u32 status_block, u32 status_err, u32 status_xfer)
+ u32 status_err, u32 status_xfer)
{
unsigned long flags;
- if (status_block & dwc->mask) {
+ if (dwc->mask) {
void (*callback)(void *param);
void *callback_param;
dev_vdbg(chan2dev(&dwc->chan), "new cyclic period llp 0x%08x\n",
channel_readl(dwc, LLP));
- dma_writel(dw, CLEAR.BLOCK, dwc->mask);
callback = dwc->cdesc->period_callback;
callback_param = dwc->cdesc->period_callback_param;
@@ -520,7 +503,6 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc,
channel_writel(dwc, CTL_LO, 0);
channel_writel(dwc, CTL_HI, 0);
- dma_writel(dw, CLEAR.BLOCK, dwc->mask);
dma_writel(dw, CLEAR.ERROR, dwc->mask);
dma_writel(dw, CLEAR.XFER, dwc->mask);
@@ -537,36 +519,29 @@ static void dw_dma_tasklet(unsigned long data)
{
struct dw_dma *dw = (struct dw_dma *)data;
struct dw_dma_chan *dwc;
- u32 status_block;
u32 status_xfer;
u32 status_err;
int i;
- status_block = dma_readl(dw, RAW.BLOCK);
status_xfer = dma_readl(dw, RAW.XFER);
status_err = dma_readl(dw, RAW.ERROR);
- dev_vdbg(dw->dma.dev, "tasklet: status_block=%x status_err=%x\n",
- status_block, status_err);
+ dev_vdbg(dw->dma.dev, "tasklet: status_err=%x\n", status_err);
for (i = 0; i < dw->dma.chancnt; i++) {
dwc = &dw->chan[i];
if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags))
- dwc_handle_cyclic(dw, dwc, status_block, status_err,
- status_xfer);
+ dwc_handle_cyclic(dw, dwc, status_err, status_xfer);
else if (status_err & (1 << i))
dwc_handle_error(dw, dwc);
- else if ((status_block | status_xfer) & (1 << i))
+ else if (status_xfer & (1 << i))
dwc_scan_descriptors(dw, dwc);
}
/*
- * Re-enable interrupts. Block Complete interrupts are only
- * enabled if the INT_EN bit in the descriptor is set. This
- * will trigger a scan before the whole list is done.
+ * Re-enable interrupts.
*/
channel_set_bit(dw, MASK.XFER, dw->all_chan_mask);
- channel_set_bit(dw, MASK.BLOCK, dw->all_chan_mask);
channel_set_bit(dw, MASK.ERROR, dw->all_chan_mask);
}
@@ -583,7 +558,6 @@ static irqreturn_t dw_dma_interrupt(int irq, void *dev_id)
* softirq handler.
*/
channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask);
- channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask);
channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask);
status = dma_readl(dw, STATUS_INT);
@@ -594,7 +568,6 @@ static irqreturn_t dw_dma_interrupt(int irq, void *dev_id)
/* Try to recover */
channel_clear_bit(dw, MASK.XFER, (1 << 8) - 1);
- channel_clear_bit(dw, MASK.BLOCK, (1 << 8) - 1);
channel_clear_bit(dw, MASK.SRC_TRAN, (1 << 8) - 1);
channel_clear_bit(dw, MASK.DST_TRAN, (1 << 8) - 1);
channel_clear_bit(dw, MASK.ERROR, (1 << 8) - 1);
@@ -615,7 +588,7 @@ static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx)
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
- cookie = dwc_assign_cookie(dwc, desc);
+ cookie = dma_cookie_assign(tx);
/*
* REVISIT: We should attempt to chain as many descriptors as
@@ -674,7 +647,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
else
src_width = dst_width = 0;
- ctllo = DWC_DEFAULT_CTLLO(chan->private)
+ ctllo = DWC_DEFAULT_CTLLO(chan)
| DWC_CTLL_DST_WIDTH(dst_width)
| DWC_CTLL_SRC_WIDTH(src_width)
| DWC_CTLL_DST_INC
@@ -731,10 +704,11 @@ err_desc_get:
static struct dma_async_tx_descriptor *
dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
- unsigned long flags)
+ unsigned long flags, void *context)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma_slave *dws = chan->private;
+ struct dma_slave_config *sconfig = &dwc->dma_sconfig;
struct dw_desc *prev;
struct dw_desc *first;
u32 ctllo;
@@ -750,25 +724,34 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
if (unlikely(!dws || !sg_len))
return NULL;
- reg_width = dws->reg_width;
prev = first = NULL;
switch (direction) {
case DMA_MEM_TO_DEV:
- ctllo = (DWC_DEFAULT_CTLLO(chan->private)
+ reg_width = __fls(sconfig->dst_addr_width);
+ reg = sconfig->dst_addr;
+ ctllo = (DWC_DEFAULT_CTLLO(chan)
| DWC_CTLL_DST_WIDTH(reg_width)
| DWC_CTLL_DST_FIX
- | DWC_CTLL_SRC_INC
- | DWC_CTLL_FC(dws->fc));
- reg = dws->tx_reg;
+ | DWC_CTLL_SRC_INC);
+
+ ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) :
+ DWC_CTLL_FC(DW_DMA_FC_D_M2P);
+
for_each_sg(sgl, sg, sg_len, i) {
struct dw_desc *desc;
u32 len, dlen, mem;
mem = sg_phys(sg);
len = sg_dma_len(sg);
- mem_width = 2;
- if (unlikely(mem & 3 || len & 3))
+
+ if (!((mem | len) & 7))
+ mem_width = 3;
+ else if (!((mem | len) & 3))
+ mem_width = 2;
+ else if (!((mem | len) & 1))
+ mem_width = 1;
+ else
mem_width = 0;
slave_sg_todev_fill_desc:
@@ -812,21 +795,30 @@ slave_sg_todev_fill_desc:
}
break;
case DMA_DEV_TO_MEM:
- ctllo = (DWC_DEFAULT_CTLLO(chan->private)
+ reg_width = __fls(sconfig->src_addr_width);
+ reg = sconfig->src_addr;
+ ctllo = (DWC_DEFAULT_CTLLO(chan)
| DWC_CTLL_SRC_WIDTH(reg_width)
| DWC_CTLL_DST_INC
- | DWC_CTLL_SRC_FIX
- | DWC_CTLL_FC(dws->fc));
+ | DWC_CTLL_SRC_FIX);
+
+ ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) :
+ DWC_CTLL_FC(DW_DMA_FC_D_P2M);
- reg = dws->rx_reg;
for_each_sg(sgl, sg, sg_len, i) {
struct dw_desc *desc;
u32 len, dlen, mem;
mem = sg_phys(sg);
len = sg_dma_len(sg);
- mem_width = 2;
- if (unlikely(mem & 3 || len & 3))
+
+ if (!((mem | len) & 7))
+ mem_width = 3;
+ else if (!((mem | len) & 3))
+ mem_width = 2;
+ else if (!((mem | len) & 1))
+ mem_width = 1;
+ else
mem_width = 0;
slave_sg_fromdev_fill_desc:
@@ -890,6 +882,39 @@ err_desc_get:
return NULL;
}
+/*
+ * Fix sconfig's burst size according to dw_dmac. We need to convert them as:
+ * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.
+ *
+ * NOTE: burst size 2 is not supported by controller.
+ *
+ * This can be done by finding least significant bit set: n & (n - 1)
+ */
+static inline void convert_burst(u32 *maxburst)
+{
+ if (*maxburst > 1)
+ *maxburst = fls(*maxburst) - 2;
+ else
+ *maxburst = 0;
+}
+
+static int
+set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
+{
+ struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
+
+ /* Check if it is chan is configured for slave transfers */
+ if (!chan->private)
+ return -EINVAL;
+
+ memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig));
+
+ convert_burst(&dwc->dma_sconfig.src_maxburst);
+ convert_burst(&dwc->dma_sconfig.dst_maxburst);
+
+ return 0;
+}
+
static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
unsigned long arg)
{
@@ -939,8 +964,11 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
/* Flush all pending and queued descriptors */
list_for_each_entry_safe(desc, _desc, &list, desc_node)
dwc_descriptor_complete(dwc, desc, false);
- } else
+ } else if (cmd == DMA_SLAVE_CONFIG) {
+ return set_runtime_config(chan, (struct dma_slave_config *)arg);
+ } else {
return -ENXIO;
+ }
return 0;
}
@@ -951,28 +979,17 @@ dwc_tx_status(struct dma_chan *chan,
struct dma_tx_state *txstate)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
- int ret;
-
- last_complete = dwc->completed;
- last_used = chan->cookie;
+ enum dma_status ret;
- ret = dma_async_is_complete(cookie, last_complete, last_used);
+ ret = dma_cookie_status(chan, cookie, txstate);
if (ret != DMA_SUCCESS) {
dwc_scan_descriptors(to_dw_dma(chan->device), dwc);
- last_complete = dwc->completed;
- last_used = chan->cookie;
-
- ret = dma_async_is_complete(cookie, last_complete, last_used);
+ ret = dma_cookie_status(chan, cookie, txstate);
}
if (ret != DMA_SUCCESS)
- dma_set_tx_state(txstate, last_complete, last_used,
- dwc_first_active(dwc)->len);
- else
- dma_set_tx_state(txstate, last_complete, last_used, 0);
+ dma_set_residue(txstate, dwc_first_active(dwc)->len);
if (dwc->paused)
return DMA_PAUSED;
@@ -1004,7 +1021,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan)
return -EIO;
}
- dwc->completed = chan->cookie = 1;
+ dma_cookie_init(chan);
/*
* NOTE: some controllers may have additional features that we
@@ -1068,7 +1085,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
/* Disable interrupts */
channel_clear_bit(dw, MASK.XFER, dwc->mask);
- channel_clear_bit(dw, MASK.BLOCK, dwc->mask);
channel_clear_bit(dw, MASK.ERROR, dwc->mask);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1120,7 +1136,6 @@ int dw_dma_cyclic_start(struct dma_chan *chan)
return -EBUSY;
}
- dma_writel(dw, CLEAR.BLOCK, dwc->mask);
dma_writel(dw, CLEAR.ERROR, dwc->mask);
dma_writel(dw, CLEAR.XFER, dwc->mask);
@@ -1175,11 +1190,11 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
enum dma_transfer_direction direction)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
+ struct dma_slave_config *sconfig = &dwc->dma_sconfig;
struct dw_cyclic_desc *cdesc;
struct dw_cyclic_desc *retval = NULL;
struct dw_desc *desc;
struct dw_desc *last = NULL;
- struct dw_dma_slave *dws = chan->private;
unsigned long was_cyclic;
unsigned int reg_width;
unsigned int periods;
@@ -1203,7 +1218,12 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
}
retval = ERR_PTR(-EINVAL);
- reg_width = dws->reg_width;
+
+ if (direction == DMA_MEM_TO_DEV)
+ reg_width = __ffs(sconfig->dst_addr_width);
+ else
+ reg_width = __ffs(sconfig->src_addr_width);
+
periods = buf_len / period_len;
/* Check for too big/unaligned periods and unaligned DMA buffer. */
@@ -1236,26 +1256,34 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
switch (direction) {
case DMA_MEM_TO_DEV:
- desc->lli.dar = dws->tx_reg;
+ desc->lli.dar = sconfig->dst_addr;
desc->lli.sar = buf_addr + (period_len * i);
- desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private)
+ desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan)
| DWC_CTLL_DST_WIDTH(reg_width)
| DWC_CTLL_SRC_WIDTH(reg_width)
| DWC_CTLL_DST_FIX
| DWC_CTLL_SRC_INC
- | DWC_CTLL_FC(dws->fc)
| DWC_CTLL_INT_EN);
+
+ desc->lli.ctllo |= sconfig->device_fc ?
+ DWC_CTLL_FC(DW_DMA_FC_P_M2P) :
+ DWC_CTLL_FC(DW_DMA_FC_D_M2P);
+
break;
case DMA_DEV_TO_MEM:
desc->lli.dar = buf_addr + (period_len * i);
- desc->lli.sar = dws->rx_reg;
- desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private)
+ desc->lli.sar = sconfig->src_addr;
+ desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan)
| DWC_CTLL_SRC_WIDTH(reg_width)
| DWC_CTLL_DST_WIDTH(reg_width)
| DWC_CTLL_DST_INC
| DWC_CTLL_SRC_FIX
- | DWC_CTLL_FC(dws->fc)
| DWC_CTLL_INT_EN);
+
+ desc->lli.ctllo |= sconfig->device_fc ?
+ DWC_CTLL_FC(DW_DMA_FC_P_P2M) :
+ DWC_CTLL_FC(DW_DMA_FC_D_P2M);
+
break;
default:
break;
@@ -1322,7 +1350,6 @@ void dw_dma_cyclic_free(struct dma_chan *chan)
while (dma_readl(dw, CH_EN) & dwc->mask)
cpu_relax();
- dma_writel(dw, CLEAR.BLOCK, dwc->mask);
dma_writel(dw, CLEAR.ERROR, dwc->mask);
dma_writel(dw, CLEAR.XFER, dwc->mask);
@@ -1347,7 +1374,6 @@ static void dw_dma_off(struct dw_dma *dw)
dma_writel(dw, CFG, 0);
channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask);
- channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask);
channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask);
channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask);
channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask);
@@ -1369,7 +1395,7 @@ static int __init dw_probe(struct platform_device *pdev)
int err;
int i;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS)
return -EINVAL;
@@ -1423,7 +1449,7 @@ static int __init dw_probe(struct platform_device *pdev)
struct dw_dma_chan *dwc = &dw->chan[i];
dwc->chan.device = &dw->dma;
- dwc->chan.cookie = dwc->completed = 1;
+ dma_cookie_init(&dwc->chan);
if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING)
list_add_tail(&dwc->chan.device_node,
&dw->dma.channels);
@@ -1432,7 +1458,7 @@ static int __init dw_probe(struct platform_device *pdev)
/* 7 is highest priority & 0 is lowest. */
if (pdata->chan_priority == CHAN_PRIORITY_ASCENDING)
- dwc->priority = 7 - i;
+ dwc->priority = pdata->nr_channels - i - 1;
else
dwc->priority = i;
@@ -1449,13 +1475,11 @@ static int __init dw_probe(struct platform_device *pdev)
/* Clear/disable all interrupts on all channels. */
dma_writel(dw, CLEAR.XFER, dw->all_chan_mask);
- dma_writel(dw, CLEAR.BLOCK, dw->all_chan_mask);
dma_writel(dw, CLEAR.SRC_TRAN, dw->all_chan_mask);
dma_writel(dw, CLEAR.DST_TRAN, dw->all_chan_mask);
dma_writel(dw, CLEAR.ERROR, dw->all_chan_mask);
channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask);
- channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask);
channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask);
channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask);
channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask);
@@ -1562,6 +1586,10 @@ static int dw_resume_noirq(struct device *dev)
static const struct dev_pm_ops dw_dev_pm_ops = {
.suspend_noirq = dw_suspend_noirq,
.resume_noirq = dw_resume_noirq,
+ .freeze_noirq = dw_suspend_noirq,
+ .thaw_noirq = dw_resume_noirq,
+ .restore_noirq = dw_resume_noirq,
+ .poweroff_noirq = dw_suspend_noirq,
};
static struct platform_driver dw_driver = {
diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h
index 5eef694..f298f69 100644
--- a/drivers/dma/dw_dmac_regs.h
+++ b/drivers/dma/dw_dmac_regs.h
@@ -13,6 +13,18 @@
#define DW_DMA_MAX_NR_CHANNELS 8
+/* flow controller */
+enum dw_dma_fc {
+ DW_DMA_FC_D_M2M,
+ DW_DMA_FC_D_M2P,
+ DW_DMA_FC_D_P2M,
+ DW_DMA_FC_D_P2P,
+ DW_DMA_FC_P_P2M,
+ DW_DMA_FC_SP_P2P,
+ DW_DMA_FC_P_M2P,
+ DW_DMA_FC_DP_P2P,
+};
+
/*
* Redefine this macro to handle differences between 32- and 64-bit
* addressing, big vs. little endian, etc.
@@ -146,13 +158,15 @@ struct dw_dma_chan {
/* these other elements are all protected by lock */
unsigned long flags;
- dma_cookie_t completed;
struct list_head active_list;
struct list_head queue;
struct list_head free_list;
struct dw_cyclic_desc *cdesc;
unsigned int descs_allocated;
+
+ /* configuration passed via DMA_SLAVE_CONFIG */
+ struct dma_slave_config dma_sconfig;
};
static inline struct dw_dma_chan_regs __iomem *
diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index 59e7a96..e6f133b 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -28,6 +28,8 @@
#include <mach/dma.h>
+#include "dmaengine.h"
+
/* M2P registers */
#define M2P_CONTROL 0x0000
#define M2P_CONTROL_STALLINT BIT(0)
@@ -122,7 +124,6 @@ struct ep93xx_dma_desc {
* @lock: lock protecting the fields following
* @flags: flags for the channel
* @buffer: which buffer to use next (0/1)
- * @last_completed: last completed cookie value
* @active: flattened chain of descriptors currently being processed
* @queue: pending descriptors which are handled next
* @free_list: list of free descriptors which can be used
@@ -157,7 +158,6 @@ struct ep93xx_dma_chan {
#define EP93XX_DMA_IS_CYCLIC 0
int buffer;
- dma_cookie_t last_completed;
struct list_head active;
struct list_head queue;
struct list_head free_list;
@@ -703,7 +703,7 @@ static void ep93xx_dma_tasklet(unsigned long data)
desc = ep93xx_dma_get_active(edmac);
if (desc) {
if (desc->complete) {
- edmac->last_completed = desc->txd.cookie;
+ dma_cookie_complete(&desc->txd);
list_splice_init(&edmac->active, &list);
}
callback = desc->txd.callback;
@@ -783,17 +783,10 @@ static dma_cookie_t ep93xx_dma_tx_submit(struct dma_async_tx_descriptor *tx)
unsigned long flags;
spin_lock_irqsave(&edmac->lock, flags);
-
- cookie = edmac->chan.cookie;
-
- if (++cookie < 0)
- cookie = 1;
+ cookie = dma_cookie_assign(tx);
desc = container_of(tx, struct ep93xx_dma_desc, txd);
- edmac->chan.cookie = cookie;
- desc->txd.cookie = cookie;
-
/*
* If nothing is currently prosessed, we push this descriptor
* directly to the hardware. Otherwise we put the descriptor
@@ -861,8 +854,7 @@ static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan)
goto fail_clk_disable;
spin_lock_irq(&edmac->lock);
- edmac->last_completed = 1;
- edmac->chan.cookie = 1;
+ dma_cookie_init(&edmac->chan);
ret = edmac->edma->hw_setup(edmac);
spin_unlock_irq(&edmac->lock);
@@ -983,13 +975,14 @@ fail:
* @sg_len: number of entries in @sgl
* @dir: direction of tha DMA transfer
* @flags: flags for the descriptor
+ * @context: operation context (ignored)
*
* Returns a valid DMA descriptor or %NULL in case of failure.
*/
static struct dma_async_tx_descriptor *
ep93xx_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction dir,
- unsigned long flags)
+ unsigned long flags, void *context)
{
struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
struct ep93xx_dma_desc *desc, *first;
@@ -1056,6 +1049,7 @@ fail:
* @buf_len: length of the buffer (in bytes)
* @period_len: lenght of a single period
* @dir: direction of the operation
+ * @context: operation context (ignored)
*
* Prepares a descriptor for cyclic DMA operation. This means that once the
* descriptor is submitted, we will be submitting in a @period_len sized
@@ -1068,7 +1062,7 @@ fail:
static struct dma_async_tx_descriptor *
ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
size_t buf_len, size_t period_len,
- enum dma_transfer_direction dir)
+ enum dma_transfer_direction dir, void *context)
{
struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
struct ep93xx_dma_desc *desc, *first;
@@ -1248,18 +1242,13 @@ static enum dma_status ep93xx_dma_tx_status(struct dma_chan *chan,
struct dma_tx_state *state)
{
struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
- dma_cookie_t last_used, last_completed;
enum dma_status ret;
unsigned long flags;
spin_lock_irqsave(&edmac->lock, flags);
- last_used = chan->cookie;
- last_completed = edmac->last_completed;
+ ret = dma_cookie_status(chan, cookie, state);
spin_unlock_irqrestore(&edmac->lock, flags);
- ret = dma_async_is_complete(cookie, last_completed, last_used);
- dma_set_tx_state(state, last_completed, last_used, 0);
-
return ret;
}
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index b98070c..8f84761 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -35,6 +35,7 @@
#include <linux/dmapool.h>
#include <linux/of_platform.h>
+#include "dmaengine.h"
#include "fsldma.h"
#define chan_dbg(chan, fmt, arg...) \
@@ -413,17 +414,10 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
* assign cookies to all of the software descriptors
* that make up this transaction
*/
- cookie = chan->common.cookie;
list_for_each_entry(child, &desc->tx_list, node) {
- cookie++;
- if (cookie < DMA_MIN_COOKIE)
- cookie = DMA_MIN_COOKIE;
-
- child->async_tx.cookie = cookie;
+ cookie = dma_cookie_assign(&child->async_tx);
}
- chan->common.cookie = cookie;
-
/* put this transaction onto the tail of the pending queue */
append_ld_queue(chan, desc);
@@ -765,6 +759,7 @@ fail:
* @sg_len: number of entries in @scatterlist
* @direction: DMA direction
* @flags: DMAEngine flags
+ * @context: transaction context (ignored)
*
* Prepare a set of descriptors for a DMA_SLAVE transaction. Following the
* DMA_SLAVE API, this gets the device-specific information from the
@@ -772,7 +767,8 @@ fail:
*/
static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg(
struct dma_chan *dchan, struct scatterlist *sgl, unsigned int sg_len,
- enum dma_transfer_direction direction, unsigned long flags)
+ enum dma_transfer_direction direction, unsigned long flags,
+ void *context)
{
/*
* This operation is not supported on the Freescale DMA controller
@@ -984,19 +980,14 @@ static enum dma_status fsl_tx_status(struct dma_chan *dchan,
struct dma_tx_state *txstate)
{
struct fsldma_chan *chan = to_fsl_chan(dchan);
- dma_cookie_t last_complete;
- dma_cookie_t last_used;
+ enum dma_status ret;
unsigned long flags;
spin_lock_irqsave(&chan->desc_lock, flags);
-
- last_complete = chan->completed_cookie;
- last_used = dchan->cookie;
-
+ ret = dma_cookie_status(dchan, cookie, txstate);
spin_unlock_irqrestore(&chan->desc_lock, flags);
- dma_set_tx_state(txstate, last_complete, last_used, 0);
- return dma_async_is_complete(cookie, last_complete, last_used);
+ return ret;
}
/*----------------------------------------------------------------------------*/
@@ -1087,8 +1078,8 @@ static void dma_do_tasklet(unsigned long data)
desc = to_fsl_desc(chan->ld_running.prev);
cookie = desc->async_tx.cookie;
+ dma_cookie_complete(&desc->async_tx);
- chan->completed_cookie = cookie;
chan_dbg(chan, "completed_cookie=%d\n", cookie);
}
@@ -1303,6 +1294,7 @@ static int __devinit fsl_dma_chan_probe(struct fsldma_device *fdev,
chan->idle = true;
chan->common.device = &fdev->common;
+ dma_cookie_init(&chan->common);
/* find the IRQ line, if it exists in the device tree */
chan->irq = irq_of_parse_and_map(node, 0);
diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h
index 9cb5aa5..f5c3879 100644
--- a/drivers/dma/fsldma.h
+++ b/drivers/dma/fsldma.h
@@ -137,7 +137,6 @@ struct fsldma_device {
struct fsldma_chan {
char name[8]; /* Channel name */
struct fsldma_chan_regs __iomem *regs;
- dma_cookie_t completed_cookie; /* The maximum cookie completed */
spinlock_t desc_lock; /* Descriptor operation lock */
struct list_head ld_pending; /* Link descriptors queue */
struct list_head ld_running; /* Link descriptors queue */
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index 38586ba..a45b5d2 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -5,6 +5,7 @@
* found on i.MX1/21/27
*
* Copyright 2010 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright 2012 Javier Martin, Vista Silicon <javier.martin@vista-silicon.com>
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
@@ -22,37 +23,159 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
+#include <linux/clk.h>
#include <linux/dmaengine.h>
#include <linux/module.h>
#include <asm/irq.h>
-#include <mach/dma-v1.h>
+#include <mach/dma.h>
#include <mach/hardware.h>
+#include "dmaengine.h"
+#define IMXDMA_MAX_CHAN_DESCRIPTORS 16
+#define IMX_DMA_CHANNELS 16
+
+#define IMX_DMA_2D_SLOTS 2
+#define IMX_DMA_2D_SLOT_A 0
+#define IMX_DMA_2D_SLOT_B 1
+
+#define IMX_DMA_LENGTH_LOOP ((unsigned int)-1)
+#define IMX_DMA_MEMSIZE_32 (0 << 4)
+#define IMX_DMA_MEMSIZE_8 (1 << 4)
+#define IMX_DMA_MEMSIZE_16 (2 << 4)
+#define IMX_DMA_TYPE_LINEAR (0 << 10)
+#define IMX_DMA_TYPE_2D (1 << 10)
+#define IMX_DMA_TYPE_FIFO (2 << 10)
+
+#define IMX_DMA_ERR_BURST (1 << 0)
+#define IMX_DMA_ERR_REQUEST (1 << 1)
+#define IMX_DMA_ERR_TRANSFER (1 << 2)
+#define IMX_DMA_ERR_BUFFER (1 << 3)
+#define IMX_DMA_ERR_TIMEOUT (1 << 4)
+
+#define DMA_DCR 0x00 /* Control Register */
+#define DMA_DISR 0x04 /* Interrupt status Register */
+#define DMA_DIMR 0x08 /* Interrupt mask Register */
+#define DMA_DBTOSR 0x0c /* Burst timeout status Register */
+#define DMA_DRTOSR 0x10 /* Request timeout Register */
+#define DMA_DSESR 0x14 /* Transfer Error Status Register */
+#define DMA_DBOSR 0x18 /* Buffer overflow status Register */
+#define DMA_DBTOCR 0x1c /* Burst timeout control Register */
+#define DMA_WSRA 0x40 /* W-Size Register A */
+#define DMA_XSRA 0x44 /* X-Size Register A */
+#define DMA_YSRA 0x48 /* Y-Size Register A */
+#define DMA_WSRB 0x4c /* W-Size Register B */
+#define DMA_XSRB 0x50 /* X-Size Register B */
+#define DMA_YSRB 0x54 /* Y-Size Register B */
+#define DMA_SAR(x) (0x80 + ((x) << 6)) /* Source Address Registers */
+#define DMA_DAR(x) (0x84 + ((x) << 6)) /* Destination Address Registers */
+#define DMA_CNTR(x) (0x88 + ((x) << 6)) /* Count Registers */
+#define DMA_CCR(x) (0x8c + ((x) << 6)) /* Control Registers */
+#define DMA_RSSR(x) (0x90 + ((x) << 6)) /* Request source select Registers */
+#define DMA_BLR(x) (0x94 + ((x) << 6)) /* Burst length Registers */
+#define DMA_RTOR(x) (0x98 + ((x) << 6)) /* Request timeout Registers */
+#define DMA_BUCR(x) (0x98 + ((x) << 6)) /* Bus Utilization Registers */
+#define DMA_CCNR(x) (0x9C + ((x) << 6)) /* Channel counter Registers */
+
+#define DCR_DRST (1<<1)
+#define DCR_DEN (1<<0)
+#define DBTOCR_EN (1<<15)
+#define DBTOCR_CNT(x) ((x) & 0x7fff)
+#define CNTR_CNT(x) ((x) & 0xffffff)
+#define CCR_ACRPT (1<<14)
+#define CCR_DMOD_LINEAR (0x0 << 12)
+#define CCR_DMOD_2D (0x1 << 12)
+#define CCR_DMOD_FIFO (0x2 << 12)
+#define CCR_DMOD_EOBFIFO (0x3 << 12)
+#define CCR_SMOD_LINEAR (0x0 << 10)
+#define CCR_SMOD_2D (0x1 << 10)
+#define CCR_SMOD_FIFO (0x2 << 10)
+#define CCR_SMOD_EOBFIFO (0x3 << 10)
+#define CCR_MDIR_DEC (1<<9)
+#define CCR_MSEL_B (1<<8)
+#define CCR_DSIZ_32 (0x0 << 6)
+#define CCR_DSIZ_8 (0x1 << 6)
+#define CCR_DSIZ_16 (0x2 << 6)
+#define CCR_SSIZ_32 (0x0 << 4)
+#define CCR_SSIZ_8 (0x1 << 4)
+#define CCR_SSIZ_16 (0x2 << 4)
+#define CCR_REN (1<<3)
+#define CCR_RPT (1<<2)
+#define CCR_FRC (1<<1)
+#define CCR_CEN (1<<0)
+#define RTOR_EN (1<<15)
+#define RTOR_CLK (1<<14)
+#define RTOR_PSC (1<<13)
+
+enum imxdma_prep_type {
+ IMXDMA_DESC_MEMCPY,
+ IMXDMA_DESC_INTERLEAVED,
+ IMXDMA_DESC_SLAVE_SG,
+ IMXDMA_DESC_CYCLIC,
+};
+
+struct imx_dma_2d_config {
+ u16 xsr;
+ u16 ysr;
+ u16 wsr;
+ int count;
+};
+
+struct imxdma_desc {
+ struct list_head node;
+ struct dma_async_tx_descriptor desc;
+ enum dma_status status;
+ dma_addr_t src;
+ dma_addr_t dest;
+ size_t len;
+ enum dma_transfer_direction direction;
+ enum imxdma_prep_type type;
+ /* For memcpy and interleaved */
+ unsigned int config_port;
+ unsigned int config_mem;
+ /* For interleaved transfers */
+ unsigned int x;
+ unsigned int y;
+ unsigned int w;
+ /* For slave sg and cyclic */
+ struct scatterlist *sg;
+ unsigned int sgcount;
+};
+
struct imxdma_channel {
+ int hw_chaining;
+ struct timer_list watchdog;
struct imxdma_engine *imxdma;
unsigned int channel;
- unsigned int imxdma_channel;
+ struct tasklet_struct dma_tasklet;
+ struct list_head ld_free;
+ struct list_head ld_queue;
+ struct list_head ld_active;
+ int descs_allocated;
enum dma_slave_buswidth word_size;
dma_addr_t per_address;
u32 watermark_level;
struct dma_chan chan;
- spinlock_t lock;
struct dma_async_tx_descriptor desc;
- dma_cookie_t last_completed;
enum dma_status status;
int dma_request;
struct scatterlist *sg_list;
+ u32 ccr_from_device;
+ u32 ccr_to_device;
+ bool enabled_2d;
+ int slot_2d;
};
-#define MAX_DMA_CHANNELS 8
-
struct imxdma_engine {
struct device *dev;
struct device_dma_parameters dma_parms;
struct dma_device dma_device;
- struct imxdma_channel channel[MAX_DMA_CHANNELS];
+ void __iomem *base;
+ struct clk *dma_clk;
+ spinlock_t lock;
+ struct imx_dma_2d_config slots_2d[IMX_DMA_2D_SLOTS];
+ struct imxdma_channel channel[IMX_DMA_CHANNELS];
};
static struct imxdma_channel *to_imxdma_chan(struct dma_chan *chan)
@@ -60,36 +183,418 @@ static struct imxdma_channel *to_imxdma_chan(struct dma_chan *chan)
return container_of(chan, struct imxdma_channel, chan);
}
-static void imxdma_handle(struct imxdma_channel *imxdmac)
+static inline bool imxdma_chan_is_doing_cyclic(struct imxdma_channel *imxdmac)
+{
+ struct imxdma_desc *desc;
+
+ if (!list_empty(&imxdmac->ld_active)) {
+ desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc,
+ node);
+ if (desc->type == IMXDMA_DESC_CYCLIC)
+ return true;
+ }
+ return false;
+}
+
+
+
+static void imx_dmav1_writel(struct imxdma_engine *imxdma, unsigned val,
+ unsigned offset)
+{
+ __raw_writel(val, imxdma->base + offset);
+}
+
+static unsigned imx_dmav1_readl(struct imxdma_engine *imxdma, unsigned offset)
+{
+ return __raw_readl(imxdma->base + offset);
+}
+
+static int imxdma_hw_chain(struct imxdma_channel *imxdmac)
+{
+ if (cpu_is_mx27())
+ return imxdmac->hw_chaining;
+ else
+ return 0;
+}
+
+/*
+ * imxdma_sg_next - prepare next chunk for scatter-gather DMA emulation
+ */
+static inline int imxdma_sg_next(struct imxdma_desc *d)
{
- if (imxdmac->desc.callback)
- imxdmac->desc.callback(imxdmac->desc.callback_param);
- imxdmac->last_completed = imxdmac->desc.cookie;
+ struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan);
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ struct scatterlist *sg = d->sg;
+ unsigned long now;
+
+ now = min(d->len, sg->length);
+ if (d->len != IMX_DMA_LENGTH_LOOP)
+ d->len -= now;
+
+ if (d->direction == DMA_DEV_TO_MEM)
+ imx_dmav1_writel(imxdma, sg->dma_address,
+ DMA_DAR(imxdmac->channel));
+ else
+ imx_dmav1_writel(imxdma, sg->dma_address,
+ DMA_SAR(imxdmac->channel));
+
+ imx_dmav1_writel(imxdma, now, DMA_CNTR(imxdmac->channel));
+
+ dev_dbg(imxdma->dev, " %s channel: %d dst 0x%08x, src 0x%08x, "
+ "size 0x%08x\n", __func__, imxdmac->channel,
+ imx_dmav1_readl(imxdma, DMA_DAR(imxdmac->channel)),
+ imx_dmav1_readl(imxdma, DMA_SAR(imxdmac->channel)),
+ imx_dmav1_readl(imxdma, DMA_CNTR(imxdmac->channel)));
+
+ return now;
}
-static void imxdma_irq_handler(int channel, void *data)
+static void imxdma_enable_hw(struct imxdma_desc *d)
{
- struct imxdma_channel *imxdmac = data;
+ struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan);
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ int channel = imxdmac->channel;
+ unsigned long flags;
+
+ dev_dbg(imxdma->dev, "%s channel %d\n", __func__, channel);
+
+ local_irq_save(flags);
+
+ imx_dmav1_writel(imxdma, 1 << channel, DMA_DISR);
+ imx_dmav1_writel(imxdma, imx_dmav1_readl(imxdma, DMA_DIMR) &
+ ~(1 << channel), DMA_DIMR);
+ imx_dmav1_writel(imxdma, imx_dmav1_readl(imxdma, DMA_CCR(channel)) |
+ CCR_CEN | CCR_ACRPT, DMA_CCR(channel));
+
+ if ((cpu_is_mx21() || cpu_is_mx27()) &&
+ d->sg && imxdma_hw_chain(imxdmac)) {
+ d->sg = sg_next(d->sg);
+ if (d->sg) {
+ u32 tmp;
+ imxdma_sg_next(d);
+ tmp = imx_dmav1_readl(imxdma, DMA_CCR(channel));
+ imx_dmav1_writel(imxdma, tmp | CCR_RPT | CCR_ACRPT,
+ DMA_CCR(channel));
+ }
+ }
+
+ local_irq_restore(flags);
+}
+
+static void imxdma_disable_hw(struct imxdma_channel *imxdmac)
+{
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ int channel = imxdmac->channel;
+ unsigned long flags;
+
+ dev_dbg(imxdma->dev, "%s channel %d\n", __func__, channel);
+
+ if (imxdma_hw_chain(imxdmac))
+ del_timer(&imxdmac->watchdog);
+
+ local_irq_save(flags);
+ imx_dmav1_writel(imxdma, imx_dmav1_readl(imxdma, DMA_DIMR) |
+ (1 << channel), DMA_DIMR);
+ imx_dmav1_writel(imxdma, imx_dmav1_readl(imxdma, DMA_CCR(channel)) &
+ ~CCR_CEN, DMA_CCR(channel));
+ imx_dmav1_writel(imxdma, 1 << channel, DMA_DISR);
+ local_irq_restore(flags);
+}
+
+static void imxdma_watchdog(unsigned long data)
+{
+ struct imxdma_channel *imxdmac = (struct imxdma_channel *)data;
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ int channel = imxdmac->channel;
+
+ imx_dmav1_writel(imxdma, 0, DMA_CCR(channel));
- imxdmac->status = DMA_SUCCESS;
- imxdma_handle(imxdmac);
+ /* Tasklet watchdog error handler */
+ tasklet_schedule(&imxdmac->dma_tasklet);
+ dev_dbg(imxdma->dev, "channel %d: watchdog timeout!\n",
+ imxdmac->channel);
}
-static void imxdma_err_handler(int channel, void *data, int error)
+static irqreturn_t imxdma_err_handler(int irq, void *dev_id)
{
- struct imxdma_channel *imxdmac = data;
+ struct imxdma_engine *imxdma = dev_id;
+ unsigned int err_mask;
+ int i, disr;
+ int errcode;
+
+ disr = imx_dmav1_readl(imxdma, DMA_DISR);
+
+ err_mask = imx_dmav1_readl(imxdma, DMA_DBTOSR) |
+ imx_dmav1_readl(imxdma, DMA_DRTOSR) |
+ imx_dmav1_readl(imxdma, DMA_DSESR) |
+ imx_dmav1_readl(imxdma, DMA_DBOSR);
+
+ if (!err_mask)
+ return IRQ_HANDLED;
+
+ imx_dmav1_writel(imxdma, disr & err_mask, DMA_DISR);
+
+ for (i = 0; i < IMX_DMA_CHANNELS; i++) {
+ if (!(err_mask & (1 << i)))
+ continue;
+ errcode = 0;
+
+ if (imx_dmav1_readl(imxdma, DMA_DBTOSR) & (1 << i)) {
+ imx_dmav1_writel(imxdma, 1 << i, DMA_DBTOSR);
+ errcode |= IMX_DMA_ERR_BURST;
+ }
+ if (imx_dmav1_readl(imxdma, DMA_DRTOSR) & (1 << i)) {
+ imx_dmav1_writel(imxdma, 1 << i, DMA_DRTOSR);
+ errcode |= IMX_DMA_ERR_REQUEST;
+ }
+ if (imx_dmav1_readl(imxdma, DMA_DSESR) & (1 << i)) {
+ imx_dmav1_writel(imxdma, 1 << i, DMA_DSESR);
+ errcode |= IMX_DMA_ERR_TRANSFER;
+ }
+ if (imx_dmav1_readl(imxdma, DMA_DBOSR) & (1 << i)) {
+ imx_dmav1_writel(imxdma, 1 << i, DMA_DBOSR);
+ errcode |= IMX_DMA_ERR_BUFFER;
+ }
+ /* Tasklet error handler */
+ tasklet_schedule(&imxdma->channel[i].dma_tasklet);
+
+ printk(KERN_WARNING
+ "DMA timeout on channel %d -%s%s%s%s\n", i,
+ errcode & IMX_DMA_ERR_BURST ? " burst" : "",
+ errcode & IMX_DMA_ERR_REQUEST ? " request" : "",
+ errcode & IMX_DMA_ERR_TRANSFER ? " transfer" : "",
+ errcode & IMX_DMA_ERR_BUFFER ? " buffer" : "");
+ }
+ return IRQ_HANDLED;
+}
+
+static void dma_irq_handle_channel(struct imxdma_channel *imxdmac)
+{
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ int chno = imxdmac->channel;
+ struct imxdma_desc *desc;
+
+ spin_lock(&imxdma->lock);
+ if (list_empty(&imxdmac->ld_active)) {
+ spin_unlock(&imxdma->lock);
+ goto out;
+ }
+
+ desc = list_first_entry(&imxdmac->ld_active,
+ struct imxdma_desc,
+ node);
+ spin_unlock(&imxdma->lock);
+
+ if (desc->sg) {
+ u32 tmp;
+ desc->sg = sg_next(desc->sg);
+
+ if (desc->sg) {
+ imxdma_sg_next(desc);
+
+ tmp = imx_dmav1_readl(imxdma, DMA_CCR(chno));
+
+ if (imxdma_hw_chain(imxdmac)) {
+ /* FIXME: The timeout should probably be
+ * configurable
+ */
+ mod_timer(&imxdmac->watchdog,
+ jiffies + msecs_to_jiffies(500));
+
+ tmp |= CCR_CEN | CCR_RPT | CCR_ACRPT;
+ imx_dmav1_writel(imxdma, tmp, DMA_CCR(chno));
+ } else {
+ imx_dmav1_writel(imxdma, tmp & ~CCR_CEN,
+ DMA_CCR(chno));
+ tmp |= CCR_CEN;
+ }
+
+ imx_dmav1_writel(imxdma, tmp, DMA_CCR(chno));
+
+ if (imxdma_chan_is_doing_cyclic(imxdmac))
+ /* Tasklet progression */
+ tasklet_schedule(&imxdmac->dma_tasklet);
+
+ return;
+ }
+
+ if (imxdma_hw_chain(imxdmac)) {
+ del_timer(&imxdmac->watchdog);
+ return;
+ }
+ }
+
+out:
+ imx_dmav1_writel(imxdma, 0, DMA_CCR(chno));
+ /* Tasklet irq */
+ tasklet_schedule(&imxdmac->dma_tasklet);
+}
+
+static irqreturn_t dma_irq_handler(int irq, void *dev_id)
+{
+ struct imxdma_engine *imxdma = dev_id;
+ int i, disr;
+
+ if (cpu_is_mx21() || cpu_is_mx27())
+ imxdma_err_handler(irq, dev_id);
+
+ disr = imx_dmav1_readl(imxdma, DMA_DISR);
+
+ dev_dbg(imxdma->dev, "%s called, disr=0x%08x\n", __func__, disr);
+
+ imx_dmav1_writel(imxdma, disr, DMA_DISR);
+ for (i = 0; i < IMX_DMA_CHANNELS; i++) {
+ if (disr & (1 << i))
+ dma_irq_handle_channel(&imxdma->channel[i]);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int imxdma_xfer_desc(struct imxdma_desc *d)
+{
+ struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan);
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ unsigned long flags;
+ int slot = -1;
+ int i;
+
+ /* Configure and enable */
+ switch (d->type) {
+ case IMXDMA_DESC_INTERLEAVED:
+ /* Try to get a free 2D slot */
+ spin_lock_irqsave(&imxdma->lock, flags);
+ for (i = 0; i < IMX_DMA_2D_SLOTS; i++) {
+ if ((imxdma->slots_2d[i].count > 0) &&
+ ((imxdma->slots_2d[i].xsr != d->x) ||
+ (imxdma->slots_2d[i].ysr != d->y) ||
+ (imxdma->slots_2d[i].wsr != d->w)))
+ continue;
+ slot = i;
+ break;
+ }
+ if (slot < 0)
+ return -EBUSY;
+
+ imxdma->slots_2d[slot].xsr = d->x;
+ imxdma->slots_2d[slot].ysr = d->y;
+ imxdma->slots_2d[slot].wsr = d->w;
+ imxdma->slots_2d[slot].count++;
+
+ imxdmac->slot_2d = slot;
+ imxdmac->enabled_2d = true;
+ spin_unlock_irqrestore(&imxdma->lock, flags);
+
+ if (slot == IMX_DMA_2D_SLOT_A) {
+ d->config_mem &= ~CCR_MSEL_B;
+ d->config_port &= ~CCR_MSEL_B;
+ imx_dmav1_writel(imxdma, d->x, DMA_XSRA);
+ imx_dmav1_writel(imxdma, d->y, DMA_YSRA);
+ imx_dmav1_writel(imxdma, d->w, DMA_WSRA);
+ } else {
+ d->config_mem |= CCR_MSEL_B;
+ d->config_port |= CCR_MSEL_B;
+ imx_dmav1_writel(imxdma, d->x, DMA_XSRB);
+ imx_dmav1_writel(imxdma, d->y, DMA_YSRB);
+ imx_dmav1_writel(imxdma, d->w, DMA_WSRB);
+ }
+ /*
+ * We fall-through here intentionally, since a 2D transfer is
+ * similar to MEMCPY just adding the 2D slot configuration.
+ */
+ case IMXDMA_DESC_MEMCPY:
+ imx_dmav1_writel(imxdma, d->src, DMA_SAR(imxdmac->channel));
+ imx_dmav1_writel(imxdma, d->dest, DMA_DAR(imxdmac->channel));
+ imx_dmav1_writel(imxdma, d->config_mem | (d->config_port << 2),
+ DMA_CCR(imxdmac->channel));
+
+ imx_dmav1_writel(imxdma, d->len, DMA_CNTR(imxdmac->channel));
+
+ dev_dbg(imxdma->dev, "%s channel: %d dest=0x%08x src=0x%08x "
+ "dma_length=%d\n", __func__, imxdmac->channel,
+ d->dest, d->src, d->len);
+
+ break;
+ /* Cyclic transfer is the same as slave_sg with special sg configuration. */
+ case IMXDMA_DESC_CYCLIC:
+ case IMXDMA_DESC_SLAVE_SG:
+ if (d->direction == DMA_DEV_TO_MEM) {
+ imx_dmav1_writel(imxdma, imxdmac->per_address,
+ DMA_SAR(imxdmac->channel));
+ imx_dmav1_writel(imxdma, imxdmac->ccr_from_device,
+ DMA_CCR(imxdmac->channel));
+
+ dev_dbg(imxdma->dev, "%s channel: %d sg=%p sgcount=%d "
+ "total length=%d dev_addr=0x%08x (dev2mem)\n",
+ __func__, imxdmac->channel, d->sg, d->sgcount,
+ d->len, imxdmac->per_address);
+ } else if (d->direction == DMA_MEM_TO_DEV) {
+ imx_dmav1_writel(imxdma, imxdmac->per_address,
+ DMA_DAR(imxdmac->channel));
+ imx_dmav1_writel(imxdma, imxdmac->ccr_to_device,
+ DMA_CCR(imxdmac->channel));
+
+ dev_dbg(imxdma->dev, "%s channel: %d sg=%p sgcount=%d "
+ "total length=%d dev_addr=0x%08x (mem2dev)\n",
+ __func__, imxdmac->channel, d->sg, d->sgcount,
+ d->len, imxdmac->per_address);
+ } else {
+ dev_err(imxdma->dev, "%s channel: %d bad dma mode\n",
+ __func__, imxdmac->channel);
+ return -EINVAL;
+ }
+
+ imxdma_sg_next(d);
- imxdmac->status = DMA_ERROR;
- imxdma_handle(imxdmac);
+ break;
+ default:
+ return -EINVAL;
+ }
+ imxdma_enable_hw(d);
+ return 0;
}
-static void imxdma_progression(int channel, void *data,
- struct scatterlist *sg)
+static void imxdma_tasklet(unsigned long data)
{
- struct imxdma_channel *imxdmac = data;
+ struct imxdma_channel *imxdmac = (void *)data;
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ struct imxdma_desc *desc;
- imxdmac->status = DMA_SUCCESS;
- imxdma_handle(imxdmac);
+ spin_lock(&imxdma->lock);
+
+ if (list_empty(&imxdmac->ld_active)) {
+ /* Someone might have called terminate all */
+ goto out;
+ }
+ desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc, node);
+
+ if (desc->desc.callback)
+ desc->desc.callback(desc->desc.callback_param);
+
+ dma_cookie_complete(&desc->desc);
+
+ /* If we are dealing with a cyclic descriptor keep it on ld_active */
+ if (imxdma_chan_is_doing_cyclic(imxdmac))
+ goto out;
+
+ /* Free 2D slot if it was an interleaved transfer */
+ if (imxdmac->enabled_2d) {
+ imxdma->slots_2d[imxdmac->slot_2d].count--;
+ imxdmac->enabled_2d = false;
+ }
+
+ list_move_tail(imxdmac->ld_active.next, &imxdmac->ld_free);
+
+ if (!list_empty(&imxdmac->ld_queue)) {
+ desc = list_first_entry(&imxdmac->ld_queue, struct imxdma_desc,
+ node);
+ list_move_tail(imxdmac->ld_queue.next, &imxdmac->ld_active);
+ if (imxdma_xfer_desc(desc) < 0)
+ dev_warn(imxdma->dev, "%s: channel: %d couldn't xfer desc\n",
+ __func__, imxdmac->channel);
+ }
+out:
+ spin_unlock(&imxdma->lock);
}
static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
@@ -97,13 +602,18 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct dma_slave_config *dmaengine_cfg = (void *)arg;
- int ret;
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ unsigned long flags;
unsigned int mode = 0;
switch (cmd) {
case DMA_TERMINATE_ALL:
- imxdmac->status = DMA_ERROR;
- imx_dma_disable(imxdmac->imxdma_channel);
+ imxdma_disable_hw(imxdmac);
+
+ spin_lock_irqsave(&imxdma->lock, flags);
+ list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free);
+ list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free);
+ spin_unlock_irqrestore(&imxdma->lock, flags);
return 0;
case DMA_SLAVE_CONFIG:
if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) {
@@ -128,16 +638,22 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
mode = IMX_DMA_MEMSIZE_32;
break;
}
- ret = imx_dma_config_channel(imxdmac->imxdma_channel,
- mode | IMX_DMA_TYPE_FIFO,
- IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
- imxdmac->dma_request, 1);
-
- if (ret)
- return ret;
- imx_dma_config_burstlen(imxdmac->imxdma_channel,
- imxdmac->watermark_level * imxdmac->word_size);
+ imxdmac->hw_chaining = 1;
+ if (!imxdma_hw_chain(imxdmac))
+ return -EINVAL;
+ imxdmac->ccr_from_device = (mode | IMX_DMA_TYPE_FIFO) |
+ ((IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) << 2) |
+ CCR_REN;
+ imxdmac->ccr_to_device =
+ (IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) |
+ ((mode | IMX_DMA_TYPE_FIFO) << 2) | CCR_REN;
+ imx_dmav1_writel(imxdma, imxdmac->dma_request,
+ DMA_RSSR(imxdmac->channel));
+
+ /* Set burst length */
+ imx_dmav1_writel(imxdma, imxdmac->watermark_level *
+ imxdmac->word_size, DMA_BLR(imxdmac->channel));
return 0;
default:
@@ -151,43 +667,20 @@ static enum dma_status imxdma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
- struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
- dma_cookie_t last_used;
- enum dma_status ret;
-
- last_used = chan->cookie;
-
- ret = dma_async_is_complete(cookie, imxdmac->last_completed, last_used);
- dma_set_tx_state(txstate, imxdmac->last_completed, last_used, 0);
-
- return ret;
-}
-
-static dma_cookie_t imxdma_assign_cookie(struct imxdma_channel *imxdma)
-{
- dma_cookie_t cookie = imxdma->chan.cookie;
-
- if (++cookie < 0)
- cookie = 1;
-
- imxdma->chan.cookie = cookie;
- imxdma->desc.cookie = cookie;
-
- return cookie;
+ return dma_cookie_status(chan, cookie, txstate);
}
static dma_cookie_t imxdma_tx_submit(struct dma_async_tx_descriptor *tx)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(tx->chan);
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
dma_cookie_t cookie;
+ unsigned long flags;
- spin_lock_irq(&imxdmac->lock);
-
- cookie = imxdma_assign_cookie(imxdmac);
-
- imx_dma_enable(imxdmac->imxdma_channel);
-
- spin_unlock_irq(&imxdmac->lock);
+ spin_lock_irqsave(&imxdma->lock, flags);
+ list_move_tail(imxdmac->ld_free.next, &imxdmac->ld_queue);
+ cookie = dma_cookie_assign(tx);
+ spin_unlock_irqrestore(&imxdma->lock, flags);
return cookie;
}
@@ -197,23 +690,52 @@ static int imxdma_alloc_chan_resources(struct dma_chan *chan)
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct imx_dma_data *data = chan->private;
- imxdmac->dma_request = data->dma_request;
+ if (data != NULL)
+ imxdmac->dma_request = data->dma_request;
- dma_async_tx_descriptor_init(&imxdmac->desc, chan);
- imxdmac->desc.tx_submit = imxdma_tx_submit;
- /* txd.flags will be overwritten in prep funcs */
- imxdmac->desc.flags = DMA_CTRL_ACK;
+ while (imxdmac->descs_allocated < IMXDMA_MAX_CHAN_DESCRIPTORS) {
+ struct imxdma_desc *desc;
- imxdmac->status = DMA_SUCCESS;
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ break;
+ __memzero(&desc->desc, sizeof(struct dma_async_tx_descriptor));
+ dma_async_tx_descriptor_init(&desc->desc, chan);
+ desc->desc.tx_submit = imxdma_tx_submit;
+ /* txd.flags will be overwritten in prep funcs */
+ desc->desc.flags = DMA_CTRL_ACK;
+ desc->status = DMA_SUCCESS;
+
+ list_add_tail(&desc->node, &imxdmac->ld_free);
+ imxdmac->descs_allocated++;
+ }
- return 0;
+ if (!imxdmac->descs_allocated)
+ return -ENOMEM;
+
+ return imxdmac->descs_allocated;
}
static void imxdma_free_chan_resources(struct dma_chan *chan)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ struct imxdma_desc *desc, *_desc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdma->lock, flags);
+
+ imxdma_disable_hw(imxdmac);
+ list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free);
+ list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free);
- imx_dma_disable(imxdmac->imxdma_channel);
+ spin_unlock_irqrestore(&imxdma->lock, flags);
+
+ list_for_each_entry_safe(desc, _desc, &imxdmac->ld_free, node) {
+ kfree(desc);
+ imxdmac->descs_allocated--;
+ }
+ INIT_LIST_HEAD(&imxdmac->ld_free);
if (imxdmac->sg_list) {
kfree(imxdmac->sg_list);
@@ -224,27 +746,23 @@ static void imxdma_free_chan_resources(struct dma_chan *chan)
static struct dma_async_tx_descriptor *imxdma_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
- unsigned long flags)
+ unsigned long flags, void *context)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct scatterlist *sg;
- int i, ret, dma_length = 0;
- unsigned int dmamode;
+ int i, dma_length = 0;
+ struct imxdma_desc *desc;
- if (imxdmac->status == DMA_IN_PROGRESS)
+ if (list_empty(&imxdmac->ld_free) ||
+ imxdma_chan_is_doing_cyclic(imxdmac))
return NULL;
- imxdmac->status = DMA_IN_PROGRESS;
+ desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node);
for_each_sg(sgl, sg, sg_len, i) {
dma_length += sg->length;
}
- if (direction == DMA_DEV_TO_MEM)
- dmamode = DMA_MODE_READ;
- else
- dmamode = DMA_MODE_WRITE;
-
switch (imxdmac->word_size) {
case DMA_SLAVE_BUSWIDTH_4_BYTES:
if (sgl->length & 3 || sgl->dma_address & 3)
@@ -260,37 +778,41 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg(
return NULL;
}
- ret = imx_dma_setup_sg(imxdmac->imxdma_channel, sgl, sg_len,
- dma_length, imxdmac->per_address, dmamode);
- if (ret)
- return NULL;
+ desc->type = IMXDMA_DESC_SLAVE_SG;
+ desc->sg = sgl;
+ desc->sgcount = sg_len;
+ desc->len = dma_length;
+ desc->direction = direction;
+ if (direction == DMA_DEV_TO_MEM) {
+ desc->src = imxdmac->per_address;
+ } else {
+ desc->dest = imxdmac->per_address;
+ }
+ desc->desc.callback = NULL;
+ desc->desc.callback_param = NULL;
- return &imxdmac->desc;
+ return &desc->desc;
}
static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
- size_t period_len, enum dma_transfer_direction direction)
+ size_t period_len, enum dma_transfer_direction direction,
+ void *context)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct imxdma_engine *imxdma = imxdmac->imxdma;
- int i, ret;
+ struct imxdma_desc *desc;
+ int i;
unsigned int periods = buf_len / period_len;
- unsigned int dmamode;
dev_dbg(imxdma->dev, "%s channel: %d buf_len=%d period_len=%d\n",
__func__, imxdmac->channel, buf_len, period_len);
- if (imxdmac->status == DMA_IN_PROGRESS)
+ if (list_empty(&imxdmac->ld_free) ||
+ imxdma_chan_is_doing_cyclic(imxdmac))
return NULL;
- imxdmac->status = DMA_IN_PROGRESS;
- ret = imx_dma_setup_progression_handler(imxdmac->imxdma_channel,
- imxdma_progression);
- if (ret) {
- dev_err(imxdma->dev, "Failed to setup the DMA handler\n");
- return NULL;
- }
+ desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node);
if (imxdmac->sg_list)
kfree(imxdmac->sg_list);
@@ -316,62 +838,221 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
imxdmac->sg_list[periods].page_link =
((unsigned long)imxdmac->sg_list | 0x01) & ~0x02;
- if (direction == DMA_DEV_TO_MEM)
- dmamode = DMA_MODE_READ;
- else
- dmamode = DMA_MODE_WRITE;
+ desc->type = IMXDMA_DESC_CYCLIC;
+ desc->sg = imxdmac->sg_list;
+ desc->sgcount = periods;
+ desc->len = IMX_DMA_LENGTH_LOOP;
+ desc->direction = direction;
+ if (direction == DMA_DEV_TO_MEM) {
+ desc->src = imxdmac->per_address;
+ } else {
+ desc->dest = imxdmac->per_address;
+ }
+ desc->desc.callback = NULL;
+ desc->desc.callback_param = NULL;
+
+ return &desc->desc;
+}
+
+static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy(
+ struct dma_chan *chan, dma_addr_t dest,
+ dma_addr_t src, size_t len, unsigned long flags)
+{
+ struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ struct imxdma_desc *desc;
- ret = imx_dma_setup_sg(imxdmac->imxdma_channel, imxdmac->sg_list, periods,
- IMX_DMA_LENGTH_LOOP, imxdmac->per_address, dmamode);
- if (ret)
+ dev_dbg(imxdma->dev, "%s channel: %d src=0x%x dst=0x%x len=%d\n",
+ __func__, imxdmac->channel, src, dest, len);
+
+ if (list_empty(&imxdmac->ld_free) ||
+ imxdma_chan_is_doing_cyclic(imxdmac))
return NULL;
- return &imxdmac->desc;
+ desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node);
+
+ desc->type = IMXDMA_DESC_MEMCPY;
+ desc->src = src;
+ desc->dest = dest;
+ desc->len = len;
+ desc->direction = DMA_MEM_TO_MEM;
+ desc->config_port = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR;
+ desc->config_mem = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR;
+ desc->desc.callback = NULL;
+ desc->desc.callback_param = NULL;
+
+ return &desc->desc;
+}
+
+static struct dma_async_tx_descriptor *imxdma_prep_dma_interleaved(
+ struct dma_chan *chan, struct dma_interleaved_template *xt,
+ unsigned long flags)
+{
+ struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ struct imxdma_desc *desc;
+
+ dev_dbg(imxdma->dev, "%s channel: %d src_start=0x%x dst_start=0x%x\n"
+ " src_sgl=%s dst_sgl=%s numf=%d frame_size=%d\n", __func__,
+ imxdmac->channel, xt->src_start, xt->dst_start,
+ xt->src_sgl ? "true" : "false", xt->dst_sgl ? "true" : "false",
+ xt->numf, xt->frame_size);
+
+ if (list_empty(&imxdmac->ld_free) ||
+ imxdma_chan_is_doing_cyclic(imxdmac))
+ return NULL;
+
+ if (xt->frame_size != 1 || xt->numf <= 0 || xt->dir != DMA_MEM_TO_MEM)
+ return NULL;
+
+ desc = list_first_entry(&imxdmac->ld_free, struct imxdma_desc, node);
+
+ desc->type = IMXDMA_DESC_INTERLEAVED;
+ desc->src = xt->src_start;
+ desc->dest = xt->dst_start;
+ desc->x = xt->sgl[0].size;
+ desc->y = xt->numf;
+ desc->w = xt->sgl[0].icg + desc->x;
+ desc->len = desc->x * desc->y;
+ desc->direction = DMA_MEM_TO_MEM;
+ desc->config_port = IMX_DMA_MEMSIZE_32;
+ desc->config_mem = IMX_DMA_MEMSIZE_32;
+ if (xt->src_sgl)
+ desc->config_mem |= IMX_DMA_TYPE_2D;
+ if (xt->dst_sgl)
+ desc->config_port |= IMX_DMA_TYPE_2D;
+ desc->desc.callback = NULL;
+ desc->desc.callback_param = NULL;
+
+ return &desc->desc;
}
static void imxdma_issue_pending(struct dma_chan *chan)
{
- /*
- * Nothing to do. We only have a single descriptor
- */
+ struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
+ struct imxdma_engine *imxdma = imxdmac->imxdma;
+ struct imxdma_desc *desc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdma->lock, flags);
+ if (list_empty(&imxdmac->ld_active) &&
+ !list_empty(&imxdmac->ld_queue)) {
+ desc = list_first_entry(&imxdmac->ld_queue,
+ struct imxdma_desc, node);
+
+ if (imxdma_xfer_desc(desc) < 0) {
+ dev_warn(imxdma->dev,
+ "%s: channel: %d couldn't issue DMA xfer\n",
+ __func__, imxdmac->channel);
+ } else {
+ list_move_tail(imxdmac->ld_queue.next,
+ &imxdmac->ld_active);
+ }
+ }
+ spin_unlock_irqrestore(&imxdma->lock, flags);
}
static int __init imxdma_probe(struct platform_device *pdev)
-{
+ {
struct imxdma_engine *imxdma;
int ret, i;
+
imxdma = kzalloc(sizeof(*imxdma), GFP_KERNEL);
if (!imxdma)
return -ENOMEM;
+ if (cpu_is_mx1()) {
+ imxdma->base = MX1_IO_ADDRESS(MX1_DMA_BASE_ADDR);
+ } else if (cpu_is_mx21()) {
+ imxdma->base = MX21_IO_ADDRESS(MX21_DMA_BASE_ADDR);
+ } else if (cpu_is_mx27()) {
+ imxdma->base = MX27_IO_ADDRESS(MX27_DMA_BASE_ADDR);
+ } else {
+ kfree(imxdma);
+ return 0;
+ }
+
+ imxdma->dma_clk = clk_get(NULL, "dma");
+ if (IS_ERR(imxdma->dma_clk))
+ return PTR_ERR(imxdma->dma_clk);
+ clk_enable(imxdma->dma_clk);
+
+ /* reset DMA module */
+ imx_dmav1_writel(imxdma, DCR_DRST, DMA_DCR);
+
+ if (cpu_is_mx1()) {
+ ret = request_irq(MX1_DMA_INT, dma_irq_handler, 0, "DMA", imxdma);
+ if (ret) {
+ dev_warn(imxdma->dev, "Can't register IRQ for DMA\n");
+ kfree(imxdma);
+ return ret;
+ }
+
+ ret = request_irq(MX1_DMA_ERR, imxdma_err_handler, 0, "DMA", imxdma);
+ if (ret) {
+ dev_warn(imxdma->dev, "Can't register ERRIRQ for DMA\n");
+ free_irq(MX1_DMA_INT, NULL);
+ kfree(imxdma);
+ return ret;
+ }
+ }
+
+ /* enable DMA module */
+ imx_dmav1_writel(imxdma, DCR_DEN, DMA_DCR);
+
+ /* clear all interrupts */
+ imx_dmav1_writel(imxdma, (1 << IMX_DMA_CHANNELS) - 1, DMA_DISR);
+
+ /* disable interrupts */
+ imx_dmav1_writel(imxdma, (1 << IMX_DMA_CHANNELS) - 1, DMA_DIMR);
+
INIT_LIST_HEAD(&imxdma->dma_device.channels);
dma_cap_set(DMA_SLAVE, imxdma->dma_device.cap_mask);
dma_cap_set(DMA_CYCLIC, imxdma->dma_device.cap_mask);
+ dma_cap_set(DMA_MEMCPY, imxdma->dma_device.cap_mask);
+ dma_cap_set(DMA_INTERLEAVE, imxdma->dma_device.cap_mask);
+
+ /* Initialize 2D global parameters */
+ for (i = 0; i < IMX_DMA_2D_SLOTS; i++)
+ imxdma->slots_2d[i].count = 0;
+
+ spin_lock_init(&imxdma->lock);
/* Initialize channel parameters */
- for (i = 0; i < MAX_DMA_CHANNELS; i++) {
+ for (i = 0; i < IMX_DMA_CHANNELS; i++) {
struct imxdma_channel *imxdmac = &imxdma->channel[i];
- imxdmac->imxdma_channel = imx_dma_request_by_prio("dmaengine",
- DMA_PRIO_MEDIUM);
- if ((int)imxdmac->channel < 0) {
- ret = -ENODEV;
- goto err_init;
+ if (cpu_is_mx21() || cpu_is_mx27()) {
+ ret = request_irq(MX2x_INT_DMACH0 + i,
+ dma_irq_handler, 0, "DMA", imxdma);
+ if (ret) {
+ dev_warn(imxdma->dev, "Can't register IRQ %d "
+ "for DMA channel %d\n",
+ MX2x_INT_DMACH0 + i, i);
+ goto err_init;
+ }
+ init_timer(&imxdmac->watchdog);
+ imxdmac->watchdog.function = &imxdma_watchdog;
+ imxdmac->watchdog.data = (unsigned long)imxdmac;
}
- imx_dma_setup_handlers(imxdmac->imxdma_channel,
- imxdma_irq_handler, imxdma_err_handler, imxdmac);
-
imxdmac->imxdma = imxdma;
- spin_lock_init(&imxdmac->lock);
+ INIT_LIST_HEAD(&imxdmac->ld_queue);
+ INIT_LIST_HEAD(&imxdmac->ld_free);
+ INIT_LIST_HEAD(&imxdmac->ld_active);
+
+ tasklet_init(&imxdmac->dma_tasklet, imxdma_tasklet,
+ (unsigned long)imxdmac);
imxdmac->chan.device = &imxdma->dma_device;
+ dma_cookie_init(&imxdmac->chan);
imxdmac->channel = i;
/* Add the channel to the DMAC list */
- list_add_tail(&imxdmac->chan.device_node, &imxdma->dma_device.channels);
+ list_add_tail(&imxdmac->chan.device_node,
+ &imxdma->dma_device.channels);
}
imxdma->dev = &pdev->dev;
@@ -382,11 +1063,14 @@ static int __init imxdma_probe(struct platform_device *pdev)
imxdma->dma_device.device_tx_status = imxdma_tx_status;
imxdma->dma_device.device_prep_slave_sg = imxdma_prep_slave_sg;
imxdma->dma_device.device_prep_dma_cyclic = imxdma_prep_dma_cyclic;
+ imxdma->dma_device.device_prep_dma_memcpy = imxdma_prep_dma_memcpy;
+ imxdma->dma_device.device_prep_interleaved_dma = imxdma_prep_dma_interleaved;
imxdma->dma_device.device_control = imxdma_control;
imxdma->dma_device.device_issue_pending = imxdma_issue_pending;
platform_set_drvdata(pdev, imxdma);
+ imxdma->dma_device.copy_align = 2; /* 2^2 = 4 bytes alignment */
imxdma->dma_device.dev->dma_parms = &imxdma->dma_parms;
dma_set_max_seg_size(imxdma->dma_device.dev, 0xffffff);
@@ -399,9 +1083,13 @@ static int __init imxdma_probe(struct platform_device *pdev)
return 0;
err_init:
- while (--i >= 0) {
- struct imxdma_channel *imxdmac = &imxdma->channel[i];
- imx_dma_free(imxdmac->imxdma_channel);
+
+ if (cpu_is_mx21() || cpu_is_mx27()) {
+ while (--i >= 0)
+ free_irq(MX2x_INT_DMACH0 + i, NULL);
+ } else if cpu_is_mx1() {
+ free_irq(MX1_DMA_INT, NULL);
+ free_irq(MX1_DMA_ERR, NULL);
}
kfree(imxdma);
@@ -415,10 +1103,12 @@ static int __exit imxdma_remove(struct platform_device *pdev)
dma_async_device_unregister(&imxdma->dma_device);
- for (i = 0; i < MAX_DMA_CHANNELS; i++) {
- struct imxdma_channel *imxdmac = &imxdma->channel[i];
-
- imx_dma_free(imxdmac->imxdma_channel);
+ if (cpu_is_mx21() || cpu_is_mx27()) {
+ for (i = 0; i < IMX_DMA_CHANNELS; i++)
+ free_irq(MX2x_INT_DMACH0 + i, NULL);
+ } else if cpu_is_mx1() {
+ free_irq(MX1_DMA_INT, NULL);
+ free_irq(MX1_DMA_ERR, NULL);
}
kfree(imxdma);
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 63540d3..d3e38e2 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -20,6 +20,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/bitops.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
@@ -41,6 +42,8 @@
#include <mach/dma.h>
#include <mach/hardware.h>
+#include "dmaengine.h"
+
/* SDMA registers */
#define SDMA_H_C0PTR 0x000
#define SDMA_H_INTR 0x004
@@ -259,19 +262,18 @@ struct sdma_channel {
unsigned int pc_from_device, pc_to_device;
unsigned long flags;
dma_addr_t per_address;
- u32 event_mask0, event_mask1;
- u32 watermark_level;
+ unsigned long event_mask[2];
+ unsigned long watermark_level;
u32 shp_addr, per_addr;
struct dma_chan chan;
spinlock_t lock;
struct dma_async_tx_descriptor desc;
- dma_cookie_t last_completed;
enum dma_status status;
unsigned int chn_count;
unsigned int chn_real_count;
};
-#define IMX_DMA_SG_LOOP (1 << 0)
+#define IMX_DMA_SG_LOOP BIT(0)
#define MAX_DMA_CHANNELS 32
#define MXC_SDMA_DEFAULT_PRIORITY 1
@@ -345,9 +347,9 @@ static const struct of_device_id sdma_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, sdma_dt_ids);
-#define SDMA_H_CONFIG_DSPDMA (1 << 12) /* indicates if the DSPDMA is used */
-#define SDMA_H_CONFIG_RTD_PINS (1 << 11) /* indicates if Real-Time Debug pins are enabled */
-#define SDMA_H_CONFIG_ACR (1 << 4) /* indicates if AHB freq /core freq = 2 or 1 */
+#define SDMA_H_CONFIG_DSPDMA BIT(12) /* indicates if the DSPDMA is used */
+#define SDMA_H_CONFIG_RTD_PINS BIT(11) /* indicates if Real-Time Debug pins are enabled */
+#define SDMA_H_CONFIG_ACR BIT(4) /* indicates if AHB freq /core freq = 2 or 1 */
#define SDMA_H_CONFIG_CSM (3) /* indicates which context switch mode is selected*/
static inline u32 chnenbl_ofs(struct sdma_engine *sdma, unsigned int event)
@@ -362,37 +364,42 @@ static int sdma_config_ownership(struct sdma_channel *sdmac,
{
struct sdma_engine *sdma = sdmac->sdma;
int channel = sdmac->channel;
- u32 evt, mcu, dsp;
+ unsigned long evt, mcu, dsp;
if (event_override && mcu_override && dsp_override)
return -EINVAL;
- evt = __raw_readl(sdma->regs + SDMA_H_EVTOVR);
- mcu = __raw_readl(sdma->regs + SDMA_H_HOSTOVR);
- dsp = __raw_readl(sdma->regs + SDMA_H_DSPOVR);
+ evt = readl_relaxed(sdma->regs + SDMA_H_EVTOVR);
+ mcu = readl_relaxed(sdma->regs + SDMA_H_HOSTOVR);
+ dsp = readl_relaxed(sdma->regs + SDMA_H_DSPOVR);
if (dsp_override)
- dsp &= ~(1 << channel);
+ __clear_bit(channel, &dsp);
else
- dsp |= (1 << channel);
+ __set_bit(channel, &dsp);
if (event_override)
- evt &= ~(1 << channel);
+ __clear_bit(channel, &evt);
else
- evt |= (1 << channel);
+ __set_bit(channel, &evt);
if (mcu_override)
- mcu &= ~(1 << channel);
+ __clear_bit(channel, &mcu);
else
- mcu |= (1 << channel);
+ __set_bit(channel, &mcu);
- __raw_writel(evt, sdma->regs + SDMA_H_EVTOVR);
- __raw_writel(mcu, sdma->regs + SDMA_H_HOSTOVR);
- __raw_writel(dsp, sdma->regs + SDMA_H_DSPOVR);
+ writel_relaxed(evt, sdma->regs + SDMA_H_EVTOVR);
+ writel_relaxed(mcu, sdma->regs + SDMA_H_HOSTOVR);
+ writel_relaxed(dsp, sdma->regs + SDMA_H_DSPOVR);
return 0;
}
+static void sdma_enable_channel(struct sdma_engine *sdma, int channel)
+{
+ writel(BIT(channel), sdma->regs + SDMA_H_START);
+}
+
/*
* sdma_run_channel - run a channel and wait till it's done
*/
@@ -404,7 +411,7 @@ static int sdma_run_channel(struct sdma_channel *sdmac)
init_completion(&sdmac->done);
- __raw_writel(1 << channel, sdma->regs + SDMA_H_START);
+ sdma_enable_channel(sdma, channel);
ret = wait_for_completion_timeout(&sdmac->done, HZ);
@@ -451,12 +458,12 @@ static void sdma_event_enable(struct sdma_channel *sdmac, unsigned int event)
{
struct sdma_engine *sdma = sdmac->sdma;
int channel = sdmac->channel;
- u32 val;
+ unsigned long val;
u32 chnenbl = chnenbl_ofs(sdma, event);
- val = __raw_readl(sdma->regs + chnenbl);
- val |= (1 << channel);
- __raw_writel(val, sdma->regs + chnenbl);
+ val = readl_relaxed(sdma->regs + chnenbl);
+ __set_bit(channel, &val);
+ writel_relaxed(val, sdma->regs + chnenbl);
}
static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event)
@@ -464,11 +471,11 @@ static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event)
struct sdma_engine *sdma = sdmac->sdma;
int channel = sdmac->channel;
u32 chnenbl = chnenbl_ofs(sdma, event);
- u32 val;
+ unsigned long val;
- val = __raw_readl(sdma->regs + chnenbl);
- val &= ~(1 << channel);
- __raw_writel(val, sdma->regs + chnenbl);
+ val = readl_relaxed(sdma->regs + chnenbl);
+ __clear_bit(channel, &val);
+ writel_relaxed(val, sdma->regs + chnenbl);
}
static void sdma_handle_channel_loop(struct sdma_channel *sdmac)
@@ -522,7 +529,7 @@ static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac)
else
sdmac->status = DMA_SUCCESS;
- sdmac->last_completed = sdmac->desc.cookie;
+ dma_cookie_complete(&sdmac->desc);
if (sdmac->desc.callback)
sdmac->desc.callback(sdmac->desc.callback_param);
}
@@ -544,10 +551,10 @@ static void mxc_sdma_handle_channel(struct sdma_channel *sdmac)
static irqreturn_t sdma_int_handler(int irq, void *dev_id)
{
struct sdma_engine *sdma = dev_id;
- u32 stat;
+ unsigned long stat;
- stat = __raw_readl(sdma->regs + SDMA_H_INTR);
- __raw_writel(stat, sdma->regs + SDMA_H_INTR);
+ stat = readl_relaxed(sdma->regs + SDMA_H_INTR);
+ writel_relaxed(stat, sdma->regs + SDMA_H_INTR);
while (stat) {
int channel = fls(stat) - 1;
@@ -555,7 +562,7 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id)
mxc_sdma_handle_channel(sdmac);
- stat &= ~(1 << channel);
+ __clear_bit(channel, &stat);
}
return IRQ_HANDLED;
@@ -663,11 +670,11 @@ static int sdma_load_context(struct sdma_channel *sdmac)
return load_address;
dev_dbg(sdma->dev, "load_address = %d\n", load_address);
- dev_dbg(sdma->dev, "wml = 0x%08x\n", sdmac->watermark_level);
+ dev_dbg(sdma->dev, "wml = 0x%08x\n", (u32)sdmac->watermark_level);
dev_dbg(sdma->dev, "shp_addr = 0x%08x\n", sdmac->shp_addr);
dev_dbg(sdma->dev, "per_addr = 0x%08x\n", sdmac->per_addr);
- dev_dbg(sdma->dev, "event_mask0 = 0x%08x\n", sdmac->event_mask0);
- dev_dbg(sdma->dev, "event_mask1 = 0x%08x\n", sdmac->event_mask1);
+ dev_dbg(sdma->dev, "event_mask0 = 0x%08x\n", (u32)sdmac->event_mask[0]);
+ dev_dbg(sdma->dev, "event_mask1 = 0x%08x\n", (u32)sdmac->event_mask[1]);
mutex_lock(&sdma->channel_0_lock);
@@ -677,8 +684,8 @@ static int sdma_load_context(struct sdma_channel *sdmac)
/* Send by context the event mask,base address for peripheral
* and watermark level
*/
- context->gReg[0] = sdmac->event_mask1;
- context->gReg[1] = sdmac->event_mask0;
+ context->gReg[0] = sdmac->event_mask[1];
+ context->gReg[1] = sdmac->event_mask[0];
context->gReg[2] = sdmac->per_addr;
context->gReg[6] = sdmac->shp_addr;
context->gReg[7] = sdmac->watermark_level;
@@ -701,7 +708,7 @@ static void sdma_disable_channel(struct sdma_channel *sdmac)
struct sdma_engine *sdma = sdmac->sdma;
int channel = sdmac->channel;
- __raw_writel(1 << channel, sdma->regs + SDMA_H_STATSTOP);
+ writel_relaxed(BIT(channel), sdma->regs + SDMA_H_STATSTOP);
sdmac->status = DMA_ERROR;
}
@@ -711,13 +718,13 @@ static int sdma_config_channel(struct sdma_channel *sdmac)
sdma_disable_channel(sdmac);
- sdmac->event_mask0 = 0;
- sdmac->event_mask1 = 0;
+ sdmac->event_mask[0] = 0;
+ sdmac->event_mask[1] = 0;
sdmac->shp_addr = 0;
sdmac->per_addr = 0;
if (sdmac->event_id0) {
- if (sdmac->event_id0 > 32)
+ if (sdmac->event_id0 >= sdmac->sdma->num_events)
return -EINVAL;
sdma_event_enable(sdmac, sdmac->event_id0);
}
@@ -740,15 +747,14 @@ static int sdma_config_channel(struct sdma_channel *sdmac)
(sdmac->peripheral_type != IMX_DMATYPE_DSP)) {
/* Handle multiple event channels differently */
if (sdmac->event_id1) {
- sdmac->event_mask1 = 1 << (sdmac->event_id1 % 32);
+ sdmac->event_mask[1] = BIT(sdmac->event_id1 % 32);
if (sdmac->event_id1 > 31)
- sdmac->watermark_level |= 1 << 31;
- sdmac->event_mask0 = 1 << (sdmac->event_id0 % 32);
+ __set_bit(31, &sdmac->watermark_level);
+ sdmac->event_mask[0] = BIT(sdmac->event_id0 % 32);
if (sdmac->event_id0 > 31)
- sdmac->watermark_level |= 1 << 30;
+ __set_bit(30, &sdmac->watermark_level);
} else {
- sdmac->event_mask0 = 1 << sdmac->event_id0;
- sdmac->event_mask1 = 1 << (sdmac->event_id0 - 32);
+ __set_bit(sdmac->event_id0, sdmac->event_mask);
}
/* Watermark Level */
sdmac->watermark_level |= sdmac->watermark_level;
@@ -774,7 +780,7 @@ static int sdma_set_channel_priority(struct sdma_channel *sdmac,
return -EINVAL;
}
- __raw_writel(priority, sdma->regs + SDMA_CHNPRI_0 + 4 * channel);
+ writel_relaxed(priority, sdma->regs + SDMA_CHNPRI_0 + 4 * channel);
return 0;
}
@@ -796,8 +802,6 @@ static int sdma_request_channel(struct sdma_channel *sdmac)
sdma->channel_control[channel].base_bd_ptr = sdmac->bd_phys;
sdma->channel_control[channel].current_bd_ptr = sdmac->bd_phys;
- clk_enable(sdma->clk);
-
sdma_set_channel_priority(sdmac, MXC_SDMA_DEFAULT_PRIORITY);
init_completion(&sdmac->done);
@@ -810,24 +814,6 @@ out:
return ret;
}
-static void sdma_enable_channel(struct sdma_engine *sdma, int channel)
-{
- __raw_writel(1 << channel, sdma->regs + SDMA_H_START);
-}
-
-static dma_cookie_t sdma_assign_cookie(struct sdma_channel *sdmac)
-{
- dma_cookie_t cookie = sdmac->chan.cookie;
-
- if (++cookie < 0)
- cookie = 1;
-
- sdmac->chan.cookie = cookie;
- sdmac->desc.cookie = cookie;
-
- return cookie;
-}
-
static struct sdma_channel *to_sdma_chan(struct dma_chan *chan)
{
return container_of(chan, struct sdma_channel, chan);
@@ -837,14 +823,11 @@ static dma_cookie_t sdma_tx_submit(struct dma_async_tx_descriptor *tx)
{
unsigned long flags;
struct sdma_channel *sdmac = to_sdma_chan(tx->chan);
- struct sdma_engine *sdma = sdmac->sdma;
dma_cookie_t cookie;
spin_lock_irqsave(&sdmac->lock, flags);
- cookie = sdma_assign_cookie(sdmac);
-
- sdma_enable_channel(sdma, sdmac->channel);
+ cookie = dma_cookie_assign(tx);
spin_unlock_irqrestore(&sdmac->lock, flags);
@@ -875,11 +858,14 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan)
sdmac->peripheral_type = data->peripheral_type;
sdmac->event_id0 = data->dma_request;
- ret = sdma_set_channel_priority(sdmac, prio);
+
+ clk_enable(sdmac->sdma->clk);
+
+ ret = sdma_request_channel(sdmac);
if (ret)
return ret;
- ret = sdma_request_channel(sdmac);
+ ret = sdma_set_channel_priority(sdmac, prio);
if (ret)
return ret;
@@ -916,7 +902,7 @@ static void sdma_free_chan_resources(struct dma_chan *chan)
static struct dma_async_tx_descriptor *sdma_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
- unsigned long flags)
+ unsigned long flags, void *context)
{
struct sdma_channel *sdmac = to_sdma_chan(chan);
struct sdma_engine *sdma = sdmac->sdma;
@@ -1014,7 +1000,8 @@ err_out:
static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
- size_t period_len, enum dma_transfer_direction direction)
+ size_t period_len, enum dma_transfer_direction direction,
+ void *context)
{
struct sdma_channel *sdmac = to_sdma_chan(chan);
struct sdma_engine *sdma = sdmac->sdma;
@@ -1128,7 +1115,7 @@ static enum dma_status sdma_tx_status(struct dma_chan *chan,
last_used = chan->cookie;
- dma_set_tx_state(txstate, sdmac->last_completed, last_used,
+ dma_set_tx_state(txstate, chan->completed_cookie, last_used,
sdmac->chn_count - sdmac->chn_real_count);
return sdmac->status;
@@ -1136,9 +1123,11 @@ static enum dma_status sdma_tx_status(struct dma_chan *chan,
static void sdma_issue_pending(struct dma_chan *chan)
{
- /*
- * Nothing to do. We only have a single descriptor
- */
+ struct sdma_channel *sdmac = to_sdma_chan(chan);
+ struct sdma_engine *sdma = sdmac->sdma;
+
+ if (sdmac->status == DMA_IN_PROGRESS)
+ sdma_enable_channel(sdma, sdmac->channel);
}
#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34
@@ -1230,7 +1219,7 @@ static int __init sdma_init(struct sdma_engine *sdma)
clk_enable(sdma->clk);
/* Be sure SDMA has not started yet */
- __raw_writel(0, sdma->regs + SDMA_H_C0PTR);
+ writel_relaxed(0, sdma->regs + SDMA_H_C0PTR);
sdma->channel_control = dma_alloc_coherent(NULL,
MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) +
@@ -1253,11 +1242,11 @@ static int __init sdma_init(struct sdma_engine *sdma)
/* disable all channels */
for (i = 0; i < sdma->num_events; i++)
- __raw_writel(0, sdma->regs + chnenbl_ofs(sdma, i));
+ writel_relaxed(0, sdma->regs + chnenbl_ofs(sdma, i));
/* All channels have priority 0 */
for (i = 0; i < MAX_DMA_CHANNELS; i++)
- __raw_writel(0, sdma->regs + SDMA_CHNPRI_0 + i * 4);
+ writel_relaxed(0, sdma->regs + SDMA_CHNPRI_0 + i * 4);
ret = sdma_request_channel(&sdma->channel[0]);
if (ret)
@@ -1266,16 +1255,16 @@ static int __init sdma_init(struct sdma_engine *sdma)
sdma_config_ownership(&sdma->channel[0], false, true, false);
/* Set Command Channel (Channel Zero) */
- __raw_writel(0x4050, sdma->regs + SDMA_CHN0ADDR);
+ writel_relaxed(0x4050, sdma->regs + SDMA_CHN0ADDR);
/* Set bits of CONFIG register but with static context switching */
/* FIXME: Check whether to set ACR bit depending on clock ratios */
- __raw_writel(0, sdma->regs + SDMA_H_CONFIG);
+ writel_relaxed(0, sdma->regs + SDMA_H_CONFIG);
- __raw_writel(ccb_phys, sdma->regs + SDMA_H_C0PTR);
+ writel_relaxed(ccb_phys, sdma->regs + SDMA_H_C0PTR);
/* Set bits of CONFIG register with given context switching mode */
- __raw_writel(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG);
+ writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG);
/* Initializes channel's priorities */
sdma_set_channel_priority(&sdma->channel[0], 7);
@@ -1367,6 +1356,7 @@ static int __init sdma_probe(struct platform_device *pdev)
spin_lock_init(&sdmac->lock);
sdmac->chan.device = &sdma->dma_device;
+ dma_cookie_init(&sdmac->chan);
sdmac->channel = i;
/*
@@ -1387,7 +1377,9 @@ static int __init sdma_probe(struct platform_device *pdev)
sdma_add_scripts(sdma, pdata->script_addrs);
if (pdata) {
- sdma_get_firmware(sdma, pdata->fw_name);
+ ret = sdma_get_firmware(sdma, pdata->fw_name);
+ if (ret)
+ dev_warn(&pdev->dev, "failed to get firmware from platform data\n");
} else {
/*
* Because that device tree does not encode ROM script address,
@@ -1396,15 +1388,12 @@ static int __init sdma_probe(struct platform_device *pdev)
*/
ret = of_property_read_string(np, "fsl,sdma-ram-script-name",
&fw_name);
- if (ret) {
- dev_err(&pdev->dev, "failed to get firmware name\n");
- goto err_init;
- }
-
- ret = sdma_get_firmware(sdma, fw_name);
- if (ret) {
- dev_err(&pdev->dev, "failed to get firmware\n");
- goto err_init;
+ if (ret)
+ dev_warn(&pdev->dev, "failed to get firmware name\n");
+ else {
+ ret = sdma_get_firmware(sdma, fw_name);
+ if (ret)
+ dev_warn(&pdev->dev, "failed to get firmware from device tree\n");
}
}
diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c
index 74f70aa..c900ca7 100644
--- a/drivers/dma/intel_mid_dma.c
+++ b/drivers/dma/intel_mid_dma.c
@@ -29,6 +29,8 @@
#include <linux/intel_mid_dma.h>
#include <linux/module.h>
+#include "dmaengine.h"
+
#define MAX_CHAN 4 /*max ch across controllers*/
#include "intel_mid_dma_regs.h"
@@ -288,7 +290,7 @@ static void midc_descriptor_complete(struct intel_mid_dma_chan *midc,
struct intel_mid_dma_lli *llitem;
void *param_txd = NULL;
- midc->completed = txd->cookie;
+ dma_cookie_complete(txd);
callback_txd = txd->callback;
param_txd = txd->callback_param;
@@ -434,14 +436,7 @@ static dma_cookie_t intel_mid_dma_tx_submit(struct dma_async_tx_descriptor *tx)
dma_cookie_t cookie;
spin_lock_bh(&midc->lock);
- cookie = midc->chan.cookie;
-
- if (++cookie < 0)
- cookie = 1;
-
- midc->chan.cookie = cookie;
- desc->txd.cookie = cookie;
-
+ cookie = dma_cookie_assign(tx);
if (list_empty(&midc->active_list))
list_add_tail(&desc->desc_node, &midc->active_list);
@@ -482,31 +477,18 @@ static enum dma_status intel_mid_dma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
- struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan);
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
- int ret;
+ struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan);
+ enum dma_status ret;
- last_complete = midc->completed;
- last_used = chan->cookie;
-
- ret = dma_async_is_complete(cookie, last_complete, last_used);
+ ret = dma_cookie_status(chan, cookie, txstate);
if (ret != DMA_SUCCESS) {
spin_lock_bh(&midc->lock);
midc_scan_descriptors(to_middma_device(chan->device), midc);
spin_unlock_bh(&midc->lock);
- last_complete = midc->completed;
- last_used = chan->cookie;
-
- ret = dma_async_is_complete(cookie, last_complete, last_used);
+ ret = dma_cookie_status(chan, cookie, txstate);
}
- if (txstate) {
- txstate->last = last_complete;
- txstate->used = last_used;
- txstate->residue = 0;
- }
return ret;
}
@@ -732,13 +714,14 @@ err_desc_get:
* @sg_len: length of sg txn
* @direction: DMA transfer dirtn
* @flags: DMA flags
+ * @context: transfer context (ignored)
*
* Prepares LLI based periphral transfer
*/
static struct dma_async_tx_descriptor *intel_mid_dma_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
- unsigned long flags)
+ unsigned long flags, void *context)
{
struct intel_mid_dma_chan *midc = NULL;
struct intel_mid_dma_slave *mids = NULL;
@@ -832,7 +815,6 @@ static void intel_mid_dma_free_chan_resources(struct dma_chan *chan)
/*trying to free ch in use!!!!!*/
pr_err("ERR_MDMA: trying to free ch in use\n");
}
- pm_runtime_put(&mid->pdev->dev);
spin_lock_bh(&midc->lock);
midc->descs_allocated = 0;
list_for_each_entry_safe(desc, _desc, &midc->active_list, desc_node) {
@@ -853,6 +835,7 @@ static void intel_mid_dma_free_chan_resources(struct dma_chan *chan)
/* Disable CH interrupts */
iowrite32(MASK_INTR_REG(midc->ch_id), mid->dma_base + MASK_BLOCK);
iowrite32(MASK_INTR_REG(midc->ch_id), mid->dma_base + MASK_ERR);
+ pm_runtime_put(&mid->pdev->dev);
}
/**
@@ -886,7 +869,7 @@ static int intel_mid_dma_alloc_chan_resources(struct dma_chan *chan)
pm_runtime_put(&mid->pdev->dev);
return -EIO;
}
- midc->completed = chan->cookie = 1;
+ dma_cookie_init(chan);
spin_lock_bh(&midc->lock);
while (midc->descs_allocated < DESCS_PER_CHANNEL) {
@@ -1056,7 +1039,8 @@ static irqreturn_t intel_mid_dma_interrupt(int irq, void *data)
}
err_status &= mid->intr_mask;
if (err_status) {
- iowrite32(MASK_INTR_REG(err_status), mid->dma_base + MASK_ERR);
+ iowrite32((err_status << INT_MASK_WE),
+ mid->dma_base + MASK_ERR);
call_tasklet = 1;
}
if (call_tasklet)
@@ -1118,7 +1102,7 @@ static int mid_setup_dma(struct pci_dev *pdev)
struct intel_mid_dma_chan *midch = &dma->ch[i];
midch->chan.device = &dma->common;
- midch->chan.cookie = 1;
+ dma_cookie_init(&midch->chan);
midch->ch_id = dma->chan_base + i;
pr_debug("MDMA:Init CH %d, ID %d\n", i, midch->ch_id);
diff --git a/drivers/dma/intel_mid_dma_regs.h b/drivers/dma/intel_mid_dma_regs.h
index c83d35b..1bfa926 100644
--- a/drivers/dma/intel_mid_dma_regs.h
+++ b/drivers/dma/intel_mid_dma_regs.h
@@ -165,7 +165,6 @@ union intel_mid_dma_cfg_hi {
* @dma_base: MMIO register space DMA engine base pointer
* @ch_id: DMA channel id
* @lock: channel spinlock
- * @completed: DMA cookie
* @active_list: current active descriptors
* @queue: current queued up descriptors
* @free_list: current free descriptors
@@ -183,7 +182,6 @@ struct intel_mid_dma_chan {
void __iomem *dma_base;
int ch_id;
spinlock_t lock;
- dma_cookie_t completed;
struct list_head active_list;
struct list_head queue;
struct list_head free_list;
diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c
index a4d6cb0..31493d8 100644
--- a/drivers/dma/ioat/dma.c
+++ b/drivers/dma/ioat/dma.c
@@ -40,6 +40,8 @@
#include "registers.h"
#include "hw.h"
+#include "../dmaengine.h"
+
int ioat_pending_level = 4;
module_param(ioat_pending_level, int, 0644);
MODULE_PARM_DESC(ioat_pending_level,
@@ -107,6 +109,7 @@ void ioat_init_channel(struct ioatdma_device *device, struct ioat_chan_common *c
chan->reg_base = device->reg_base + (0x80 * (idx + 1));
spin_lock_init(&chan->cleanup_lock);
chan->common.device = dma;
+ dma_cookie_init(&chan->common);
list_add_tail(&chan->common.device_node, &dma->channels);
device->idx[idx] = chan;
init_timer(&chan->timer);
@@ -235,12 +238,7 @@ static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx)
spin_lock_bh(&ioat->desc_lock);
/* cookie incr and addition to used_list must be atomic */
- cookie = c->cookie;
- cookie++;
- if (cookie < 0)
- cookie = 1;
- c->cookie = cookie;
- tx->cookie = cookie;
+ cookie = dma_cookie_assign(tx);
dev_dbg(to_dev(&ioat->base), "%s: cookie: %d\n", __func__, cookie);
/* write address into NextDescriptor field of last desc in chain */
@@ -603,8 +601,7 @@ static void __cleanup(struct ioat_dma_chan *ioat, unsigned long phys_complete)
*/
dump_desc_dbg(ioat, desc);
if (tx->cookie) {
- chan->completed_cookie = tx->cookie;
- tx->cookie = 0;
+ dma_cookie_complete(tx);
ioat_dma_unmap(chan, tx->flags, desc->len, desc->hw);
ioat->active -= desc->hw->tx_cnt;
if (tx->callback) {
@@ -733,13 +730,15 @@ ioat_dma_tx_status(struct dma_chan *c, dma_cookie_t cookie,
{
struct ioat_chan_common *chan = to_chan_common(c);
struct ioatdma_device *device = chan->device;
+ enum dma_status ret;
- if (ioat_tx_status(c, cookie, txstate) == DMA_SUCCESS)
- return DMA_SUCCESS;
+ ret = dma_cookie_status(c, cookie, txstate);
+ if (ret == DMA_SUCCESS)
+ return ret;
device->cleanup_fn((unsigned long) c);
- return ioat_tx_status(c, cookie, txstate);
+ return dma_cookie_status(c, cookie, txstate);
}
static void ioat1_dma_start_null_desc(struct ioat_dma_chan *ioat)
diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h
index 5216c8a..c7888bc 100644
--- a/drivers/dma/ioat/dma.h
+++ b/drivers/dma/ioat/dma.h
@@ -90,7 +90,6 @@ struct ioat_chan_common {
void __iomem *reg_base;
unsigned long last_completion;
spinlock_t cleanup_lock;
- dma_cookie_t completed_cookie;
unsigned long state;
#define IOAT_COMPLETION_PENDING 0
#define IOAT_COMPLETION_ACK 1
@@ -143,28 +142,6 @@ static inline struct ioat_dma_chan *to_ioat_chan(struct dma_chan *c)
return container_of(chan, struct ioat_dma_chan, base);
}
-/**
- * ioat_tx_status - poll the status of an ioat transaction
- * @c: channel handle
- * @cookie: transaction identifier
- * @txstate: if set, updated with the transaction state
- */
-static inline enum dma_status
-ioat_tx_status(struct dma_chan *c, dma_cookie_t cookie,
- struct dma_tx_state *txstate)
-{
- struct ioat_chan_common *chan = to_chan_common(c);
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
-
- last_used = c->cookie;
- last_complete = chan->completed_cookie;
-
- dma_set_tx_state(txstate, last_complete, last_used, 0);
-
- return dma_async_is_complete(cookie, last_complete, last_used);
-}
-
/* wrapper around hardware descriptor format + additional software fields */
/**
diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c
index 5d65f83..e8e110f 100644
--- a/drivers/dma/ioat/dma_v2.c
+++ b/drivers/dma/ioat/dma_v2.c
@@ -41,6 +41,8 @@
#include "registers.h"
#include "hw.h"
+#include "../dmaengine.h"
+
int ioat_ring_alloc_order = 8;
module_param(ioat_ring_alloc_order, int, 0644);
MODULE_PARM_DESC(ioat_ring_alloc_order,
@@ -147,8 +149,7 @@ static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete)
dump_desc_dbg(ioat, desc);
if (tx->cookie) {
ioat_dma_unmap(chan, tx->flags, desc->len, desc->hw);
- chan->completed_cookie = tx->cookie;
- tx->cookie = 0;
+ dma_cookie_complete(tx);
if (tx->callback) {
tx->callback(tx->callback_param);
tx->callback = NULL;
@@ -398,13 +399,9 @@ static dma_cookie_t ioat2_tx_submit_unlock(struct dma_async_tx_descriptor *tx)
struct dma_chan *c = tx->chan;
struct ioat2_dma_chan *ioat = to_ioat2_chan(c);
struct ioat_chan_common *chan = &ioat->base;
- dma_cookie_t cookie = c->cookie;
+ dma_cookie_t cookie;
- cookie++;
- if (cookie < 0)
- cookie = 1;
- tx->cookie = cookie;
- c->cookie = cookie;
+ cookie = dma_cookie_assign(tx);
dev_dbg(to_dev(&ioat->base), "%s: cookie: %d\n", __func__, cookie);
if (!test_and_set_bit(IOAT_COMPLETION_PENDING, &chan->state))
diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c
index f519c93..2c4476c 100644
--- a/drivers/dma/ioat/dma_v3.c
+++ b/drivers/dma/ioat/dma_v3.c
@@ -61,6 +61,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/prefetch.h>
+#include "../dmaengine.h"
#include "registers.h"
#include "hw.h"
#include "dma.h"
@@ -277,9 +278,8 @@ static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete)
dump_desc_dbg(ioat, desc);
tx = &desc->txd;
if (tx->cookie) {
- chan->completed_cookie = tx->cookie;
+ dma_cookie_complete(tx);
ioat3_dma_unmap(ioat, desc, idx + i);
- tx->cookie = 0;
if (tx->callback) {
tx->callback(tx->callback_param);
tx->callback = NULL;
@@ -411,13 +411,15 @@ ioat3_tx_status(struct dma_chan *c, dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
struct ioat2_dma_chan *ioat = to_ioat2_chan(c);
+ enum dma_status ret;
- if (ioat_tx_status(c, cookie, txstate) == DMA_SUCCESS)
- return DMA_SUCCESS;
+ ret = dma_cookie_status(c, cookie, txstate);
+ if (ret == DMA_SUCCESS)
+ return ret;
ioat3_cleanup(ioat);
- return ioat_tx_status(c, cookie, txstate);
+ return dma_cookie_status(c, cookie, txstate);
}
static struct dma_async_tx_descriptor *
diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c
index faf88b7..da6c4c2 100644
--- a/drivers/dma/iop-adma.c
+++ b/drivers/dma/iop-adma.c
@@ -36,6 +36,8 @@
#include <mach/adma.h>
+#include "dmaengine.h"
+
#define to_iop_adma_chan(chan) container_of(chan, struct iop_adma_chan, common)
#define to_iop_adma_device(dev) \
container_of(dev, struct iop_adma_device, common)
@@ -317,7 +319,7 @@ static void __iop_adma_slot_cleanup(struct iop_adma_chan *iop_chan)
}
if (cookie > 0) {
- iop_chan->completed_cookie = cookie;
+ iop_chan->common.completed_cookie = cookie;
pr_debug("\tcompleted cookie %d\n", cookie);
}
}
@@ -438,18 +440,6 @@ retry:
return NULL;
}
-static dma_cookie_t
-iop_desc_assign_cookie(struct iop_adma_chan *iop_chan,
- struct iop_adma_desc_slot *desc)
-{
- dma_cookie_t cookie = iop_chan->common.cookie;
- cookie++;
- if (cookie < 0)
- cookie = 1;
- iop_chan->common.cookie = desc->async_tx.cookie = cookie;
- return cookie;
-}
-
static void iop_adma_check_threshold(struct iop_adma_chan *iop_chan)
{
dev_dbg(iop_chan->device->common.dev, "pending: %d\n",
@@ -477,7 +467,7 @@ iop_adma_tx_submit(struct dma_async_tx_descriptor *tx)
slots_per_op = grp_start->slots_per_op;
spin_lock_bh(&iop_chan->lock);
- cookie = iop_desc_assign_cookie(iop_chan, sw_desc);
+ cookie = dma_cookie_assign(tx);
old_chain_tail = list_entry(iop_chan->chain.prev,
struct iop_adma_desc_slot, chain_node);
@@ -904,24 +894,15 @@ static enum dma_status iop_adma_status(struct dma_chan *chan,
struct dma_tx_state *txstate)
{
struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan);
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
- enum dma_status ret;
-
- last_used = chan->cookie;
- last_complete = iop_chan->completed_cookie;
- dma_set_tx_state(txstate, last_complete, last_used, 0);
- ret = dma_async_is_complete(cookie, last_complete, last_used);
+ int ret;
+
+ ret = dma_cookie_status(chan, cookie, txstate);
if (ret == DMA_SUCCESS)
return ret;
iop_adma_slot_cleanup(iop_chan);
- last_used = chan->cookie;
- last_complete = iop_chan->completed_cookie;
- dma_set_tx_state(txstate, last_complete, last_used, 0);
-
- return dma_async_is_complete(cookie, last_complete, last_used);
+ return dma_cookie_status(chan, cookie, txstate);
}
static irqreturn_t iop_adma_eot_handler(int irq, void *data)
@@ -1565,6 +1546,7 @@ static int __devinit iop_adma_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&iop_chan->chain);
INIT_LIST_HEAD(&iop_chan->all_slots);
iop_chan->common.device = dma_dev;
+ dma_cookie_init(&iop_chan->common);
list_add_tail(&iop_chan->common.device_node, &dma_dev->channels);
if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) {
@@ -1642,16 +1624,12 @@ static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan)
iop_desc_set_dest_addr(grp_start, iop_chan, 0);
iop_desc_set_memcpy_src_addr(grp_start, 0);
- cookie = iop_chan->common.cookie;
- cookie++;
- if (cookie <= 1)
- cookie = 2;
+ cookie = dma_cookie_assign(&sw_desc->async_tx);
/* initialize the completed cookie to be less than
* the most recently used cookie
*/
- iop_chan->completed_cookie = cookie - 1;
- iop_chan->common.cookie = sw_desc->async_tx.cookie = cookie;
+ iop_chan->common.completed_cookie = cookie - 1;
/* channel should not be busy */
BUG_ON(iop_chan_is_busy(iop_chan));
@@ -1699,16 +1677,12 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan)
iop_desc_set_xor_src_addr(grp_start, 0, 0);
iop_desc_set_xor_src_addr(grp_start, 1, 0);
- cookie = iop_chan->common.cookie;
- cookie++;
- if (cookie <= 1)
- cookie = 2;
+ cookie = dma_cookie_assign(&sw_desc->async_tx);
/* initialize the completed cookie to be less than
* the most recently used cookie
*/
- iop_chan->completed_cookie = cookie - 1;
- iop_chan->common.cookie = sw_desc->async_tx.cookie = cookie;
+ iop_chan->common.completed_cookie = cookie - 1;
/* channel should not be busy */
BUG_ON(iop_chan_is_busy(iop_chan));
diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c
index 6212b16..62e3f8e 100644
--- a/drivers/dma/ipu/ipu_idmac.c
+++ b/drivers/dma/ipu/ipu_idmac.c
@@ -25,6 +25,7 @@
#include <mach/ipu.h>
+#include "../dmaengine.h"
#include "ipu_intern.h"
#define FS_VF_IN_VALID 0x00000002
@@ -866,14 +867,7 @@ static dma_cookie_t idmac_tx_submit(struct dma_async_tx_descriptor *tx)
dev_dbg(dev, "Submitting sg %p\n", &desc->sg[0]);
- cookie = ichan->dma_chan.cookie;
-
- if (++cookie < 0)
- cookie = 1;
-
- /* from dmaengine.h: "last cookie value returned to client" */
- ichan->dma_chan.cookie = cookie;
- tx->cookie = cookie;
+ cookie = dma_cookie_assign(tx);
/* ipu->lock can be taken under ichan->lock, but not v.v. */
spin_lock_irqsave(&ichan->lock, flags);
@@ -1295,7 +1289,7 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
/* Flip the active buffer - even if update above failed */
ichan->active_buffer = !ichan->active_buffer;
if (done)
- ichan->completed = desc->txd.cookie;
+ dma_cookie_complete(&desc->txd);
callback = desc->txd.callback;
callback_param = desc->txd.callback_param;
@@ -1341,7 +1335,8 @@ static void ipu_gc_tasklet(unsigned long arg)
/* Allocate and initialise a transfer descriptor. */
static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan,
struct scatterlist *sgl, unsigned int sg_len,
- enum dma_transfer_direction direction, unsigned long tx_flags)
+ enum dma_transfer_direction direction, unsigned long tx_flags,
+ void *context)
{
struct idmac_channel *ichan = to_idmac_chan(chan);
struct idmac_tx_desc *desc = NULL;
@@ -1510,8 +1505,7 @@ static int idmac_alloc_chan_resources(struct dma_chan *chan)
BUG_ON(chan->client_count > 1);
WARN_ON(ichan->status != IPU_CHANNEL_FREE);
- chan->cookie = 1;
- ichan->completed = -ENXIO;
+ dma_cookie_init(chan);
ret = ipu_irq_map(chan->chan_id);
if (ret < 0)
@@ -1600,9 +1594,7 @@ static void idmac_free_chan_resources(struct dma_chan *chan)
static enum dma_status idmac_tx_status(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *txstate)
{
- struct idmac_channel *ichan = to_idmac_chan(chan);
-
- dma_set_tx_state(txstate, ichan->completed, chan->cookie, 0);
+ dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, 0);
if (cookie != chan->cookie)
return DMA_ERROR;
return DMA_SUCCESS;
@@ -1638,11 +1630,10 @@ static int __init ipu_idmac_init(struct ipu *ipu)
ichan->status = IPU_CHANNEL_FREE;
ichan->sec_chan_en = false;
- ichan->completed = -ENXIO;
snprintf(ichan->eof_name, sizeof(ichan->eof_name), "IDMAC EOF %d", i);
dma_chan->device = &idmac->dma;
- dma_chan->cookie = 1;
+ dma_cookie_init(dma_chan);
dma_chan->chan_id = i;
list_add_tail(&dma_chan->device_node, &dma->channels);
}
diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c
index 4d6d4cf..2ab0a3d 100644
--- a/drivers/dma/mpc512x_dma.c
+++ b/drivers/dma/mpc512x_dma.c
@@ -44,6 +44,8 @@
#include <linux/random.h>
+#include "dmaengine.h"
+
/* Number of DMA Transfer descriptors allocated per channel */
#define MPC_DMA_DESCRIPTORS 64
@@ -188,7 +190,6 @@ struct mpc_dma_chan {
struct list_head completed;
struct mpc_dma_tcd *tcd;
dma_addr_t tcd_paddr;
- dma_cookie_t completed_cookie;
/* Lock for this structure */
spinlock_t lock;
@@ -365,7 +366,7 @@ static void mpc_dma_process_completed(struct mpc_dma *mdma)
/* Free descriptors */
spin_lock_irqsave(&mchan->lock, flags);
list_splice_tail_init(&list, &mchan->free);
- mchan->completed_cookie = last_cookie;
+ mchan->chan.completed_cookie = last_cookie;
spin_unlock_irqrestore(&mchan->lock, flags);
}
}
@@ -438,13 +439,7 @@ static dma_cookie_t mpc_dma_tx_submit(struct dma_async_tx_descriptor *txd)
mpc_dma_execute(mchan);
/* Update cookie */
- cookie = mchan->chan.cookie + 1;
- if (cookie <= 0)
- cookie = 1;
-
- mchan->chan.cookie = cookie;
- mdesc->desc.cookie = cookie;
-
+ cookie = dma_cookie_assign(txd);
spin_unlock_irqrestore(&mchan->lock, flags);
return cookie;
@@ -562,17 +557,14 @@ mpc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
+ enum dma_status ret;
unsigned long flags;
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
spin_lock_irqsave(&mchan->lock, flags);
- last_used = mchan->chan.cookie;
- last_complete = mchan->completed_cookie;
+ ret = dma_cookie_status(chan, cookie, txstate);
spin_unlock_irqrestore(&mchan->lock, flags);
- dma_set_tx_state(txstate, last_complete, last_used, 0);
- return dma_async_is_complete(cookie, last_complete, last_used);
+ return ret;
}
/* Prepare descriptor for memory to memory copy */
@@ -741,8 +733,7 @@ static int __devinit mpc_dma_probe(struct platform_device *op)
mchan = &mdma->channels[i];
mchan->chan.device = dma;
- mchan->chan.cookie = 1;
- mchan->completed_cookie = mchan->chan.cookie;
+ dma_cookie_init(&mchan->chan);
INIT_LIST_HEAD(&mchan->free);
INIT_LIST_HEAD(&mchan->prepared);
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index e779b43..fa5d55f 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -26,6 +26,8 @@
#include <linux/platform_device.h>
#include <linux/memory.h>
#include <plat/mv_xor.h>
+
+#include "dmaengine.h"
#include "mv_xor.h"
static void mv_xor_issue_pending(struct dma_chan *chan);
@@ -435,7 +437,7 @@ static void __mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan)
}
if (cookie > 0)
- mv_chan->completed_cookie = cookie;
+ mv_chan->common.completed_cookie = cookie;
}
static void
@@ -534,18 +536,6 @@ retry:
return NULL;
}
-static dma_cookie_t
-mv_desc_assign_cookie(struct mv_xor_chan *mv_chan,
- struct mv_xor_desc_slot *desc)
-{
- dma_cookie_t cookie = mv_chan->common.cookie;
-
- if (++cookie < 0)
- cookie = 1;
- mv_chan->common.cookie = desc->async_tx.cookie = cookie;
- return cookie;
-}
-
/************************ DMA engine API functions ****************************/
static dma_cookie_t
mv_xor_tx_submit(struct dma_async_tx_descriptor *tx)
@@ -563,7 +553,7 @@ mv_xor_tx_submit(struct dma_async_tx_descriptor *tx)
grp_start = sw_desc->group_head;
spin_lock_bh(&mv_chan->lock);
- cookie = mv_desc_assign_cookie(mv_chan, sw_desc);
+ cookie = dma_cookie_assign(tx);
if (list_empty(&mv_chan->chain))
list_splice_init(&sw_desc->tx_list, &mv_chan->chain);
@@ -820,27 +810,16 @@ static enum dma_status mv_xor_status(struct dma_chan *chan,
struct dma_tx_state *txstate)
{
struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
enum dma_status ret;
- last_used = chan->cookie;
- last_complete = mv_chan->completed_cookie;
- mv_chan->is_complete_cookie = cookie;
- dma_set_tx_state(txstate, last_complete, last_used, 0);
-
- ret = dma_async_is_complete(cookie, last_complete, last_used);
+ ret = dma_cookie_status(chan, cookie, txstate);
if (ret == DMA_SUCCESS) {
mv_xor_clean_completed_slots(mv_chan);
return ret;
}
mv_xor_slot_cleanup(mv_chan);
- last_used = chan->cookie;
- last_complete = mv_chan->completed_cookie;
-
- dma_set_tx_state(txstate, last_complete, last_used, 0);
- return dma_async_is_complete(cookie, last_complete, last_used);
+ return dma_cookie_status(chan, cookie, txstate);
}
static void mv_dump_xor_regs(struct mv_xor_chan *chan)
@@ -1214,6 +1193,7 @@ static int __devinit mv_xor_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&mv_chan->completed_slots);
INIT_LIST_HEAD(&mv_chan->all_slots);
mv_chan->common.device = dma_dev;
+ dma_cookie_init(&mv_chan->common);
list_add_tail(&mv_chan->common.device_node, &dma_dev->channels);
diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h
index 977b592..654876b 100644
--- a/drivers/dma/mv_xor.h
+++ b/drivers/dma/mv_xor.h
@@ -78,7 +78,6 @@ struct mv_xor_device {
/**
* struct mv_xor_chan - internal representation of a XOR channel
* @pending: allows batching of hardware operations
- * @completed_cookie: identifier for the most recently completed operation
* @lock: serializes enqueue/dequeue operations to the descriptors pool
* @mmr_base: memory mapped register base
* @idx: the index of the xor channel
@@ -93,7 +92,6 @@ struct mv_xor_device {
*/
struct mv_xor_chan {
int pending;
- dma_cookie_t completed_cookie;
spinlock_t lock; /* protects the descriptor slot pool */
void __iomem *mmr_base;
unsigned int idx;
@@ -109,7 +107,6 @@ struct mv_xor_chan {
#ifdef USE_TIMER
unsigned long cleanup_time;
u32 current_on_last_cleanup;
- dma_cookie_t is_complete_cookie;
#endif
};
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
index b06cd4c..c81ef7e 100644
--- a/drivers/dma/mxs-dma.c
+++ b/drivers/dma/mxs-dma.c
@@ -22,12 +22,14 @@
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/delay.h>
+#include <linux/fsl/mxs-dma.h>
#include <asm/irq.h>
#include <mach/mxs.h>
-#include <mach/dma.h>
#include <mach/common.h>
+#include "dmaengine.h"
+
/*
* NOTE: The term "PIO" throughout the mxs-dma implementation means
* PIO mode of mxs apbh-dma and apbx-dma. With this working mode,
@@ -111,7 +113,6 @@ struct mxs_dma_chan {
struct mxs_dma_ccw *ccw;
dma_addr_t ccw_phys;
int desc_count;
- dma_cookie_t last_completed;
enum dma_status status;
unsigned int flags;
#define MXS_DMA_SG_LOOP (1 << 0)
@@ -193,19 +194,6 @@ static void mxs_dma_resume_chan(struct mxs_dma_chan *mxs_chan)
mxs_chan->status = DMA_IN_PROGRESS;
}
-static dma_cookie_t mxs_dma_assign_cookie(struct mxs_dma_chan *mxs_chan)
-{
- dma_cookie_t cookie = mxs_chan->chan.cookie;
-
- if (++cookie < 0)
- cookie = 1;
-
- mxs_chan->chan.cookie = cookie;
- mxs_chan->desc.cookie = cookie;
-
- return cookie;
-}
-
static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan)
{
return container_of(chan, struct mxs_dma_chan, chan);
@@ -217,7 +205,7 @@ static dma_cookie_t mxs_dma_tx_submit(struct dma_async_tx_descriptor *tx)
mxs_dma_enable_chan(mxs_chan);
- return mxs_dma_assign_cookie(mxs_chan);
+ return dma_cookie_assign(tx);
}
static void mxs_dma_tasklet(unsigned long data)
@@ -274,7 +262,7 @@ static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id)
stat1 &= ~(1 << channel);
if (mxs_chan->status == DMA_SUCCESS)
- mxs_chan->last_completed = mxs_chan->desc.cookie;
+ dma_cookie_complete(&mxs_chan->desc);
/* schedule tasklet on this channel */
tasklet_schedule(&mxs_chan->tasklet);
@@ -349,10 +337,32 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan)
clk_disable_unprepare(mxs_dma->clk);
}
+/*
+ * How to use the flags for ->device_prep_slave_sg() :
+ * [1] If there is only one DMA command in the DMA chain, the code should be:
+ * ......
+ * ->device_prep_slave_sg(DMA_CTRL_ACK);
+ * ......
+ * [2] If there are two DMA commands in the DMA chain, the code should be
+ * ......
+ * ->device_prep_slave_sg(0);
+ * ......
+ * ->device_prep_slave_sg(DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ * ......
+ * [3] If there are more than two DMA commands in the DMA chain, the code
+ * should be:
+ * ......
+ * ->device_prep_slave_sg(0); // First
+ * ......
+ * ->device_prep_slave_sg(DMA_PREP_INTERRUPT [| DMA_CTRL_ACK]);
+ * ......
+ * ->device_prep_slave_sg(DMA_PREP_INTERRUPT | DMA_CTRL_ACK); // Last
+ * ......
+ */
static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
- unsigned long append)
+ unsigned long flags, void *context)
{
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
@@ -360,6 +370,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
struct scatterlist *sg;
int i, j;
u32 *pio;
+ bool append = flags & DMA_PREP_INTERRUPT;
int idx = append ? mxs_chan->desc_count : 0;
if (mxs_chan->status == DMA_IN_PROGRESS && !append)
@@ -386,7 +397,6 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
ccw->bits |= CCW_CHAIN;
ccw->bits &= ~CCW_IRQ;
ccw->bits &= ~CCW_DEC_SEM;
- ccw->bits &= ~CCW_WAIT4END;
} else {
idx = 0;
}
@@ -401,7 +411,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
ccw->bits = 0;
ccw->bits |= CCW_IRQ;
ccw->bits |= CCW_DEC_SEM;
- ccw->bits |= CCW_WAIT4END;
+ if (flags & DMA_CTRL_ACK)
+ ccw->bits |= CCW_WAIT4END;
ccw->bits |= CCW_HALT_ON_TERM;
ccw->bits |= CCW_TERM_FLUSH;
ccw->bits |= BF_CCW(sg_len, PIO_NUM);
@@ -432,7 +443,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
ccw->bits &= ~CCW_CHAIN;
ccw->bits |= CCW_IRQ;
ccw->bits |= CCW_DEC_SEM;
- ccw->bits |= CCW_WAIT4END;
+ if (flags & DMA_CTRL_ACK)
+ ccw->bits |= CCW_WAIT4END;
}
}
}
@@ -447,7 +459,8 @@ err_out:
static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
- size_t period_len, enum dma_transfer_direction direction)
+ size_t period_len, enum dma_transfer_direction direction,
+ void *context)
{
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
@@ -538,7 +551,7 @@ static enum dma_status mxs_dma_tx_status(struct dma_chan *chan,
dma_cookie_t last_used;
last_used = chan->cookie;
- dma_set_tx_state(txstate, mxs_chan->last_completed, last_used, 0);
+ dma_set_tx_state(txstate, chan->completed_cookie, last_used, 0);
return mxs_chan->status;
}
@@ -630,6 +643,7 @@ static int __init mxs_dma_probe(struct platform_device *pdev)
mxs_chan->mxs_dma = mxs_dma;
mxs_chan->chan.device = &mxs_dma->dma_device;
+ dma_cookie_init(&mxs_chan->chan);
tasklet_init(&mxs_chan->tasklet, mxs_dma_tasklet,
(unsigned long) mxs_chan);
diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c
index 823f581..65c0495 100644
--- a/drivers/dma/pch_dma.c
+++ b/drivers/dma/pch_dma.c
@@ -25,6 +25,8 @@
#include <linux/module.h>
#include <linux/pch_dma.h>
+#include "dmaengine.h"
+
#define DRV_NAME "pch-dma"
#define DMA_CTL0_DISABLE 0x0
@@ -105,7 +107,6 @@ struct pch_dma_chan {
spinlock_t lock;
- dma_cookie_t completed_cookie;
struct list_head active_list;
struct list_head queue;
struct list_head free_list;
@@ -416,20 +417,6 @@ static void pdc_advance_work(struct pch_dma_chan *pd_chan)
}
}
-static dma_cookie_t pdc_assign_cookie(struct pch_dma_chan *pd_chan,
- struct pch_dma_desc *desc)
-{
- dma_cookie_t cookie = pd_chan->chan.cookie;
-
- if (++cookie < 0)
- cookie = 1;
-
- pd_chan->chan.cookie = cookie;
- desc->txd.cookie = cookie;
-
- return cookie;
-}
-
static dma_cookie_t pd_tx_submit(struct dma_async_tx_descriptor *txd)
{
struct pch_dma_desc *desc = to_pd_desc(txd);
@@ -437,7 +424,7 @@ static dma_cookie_t pd_tx_submit(struct dma_async_tx_descriptor *txd)
dma_cookie_t cookie;
spin_lock(&pd_chan->lock);
- cookie = pdc_assign_cookie(pd_chan, desc);
+ cookie = dma_cookie_assign(txd);
if (list_empty(&pd_chan->active_list)) {
list_add_tail(&desc->desc_node, &pd_chan->active_list);
@@ -544,7 +531,7 @@ static int pd_alloc_chan_resources(struct dma_chan *chan)
spin_lock_irq(&pd_chan->lock);
list_splice(&tmp_list, &pd_chan->free_list);
pd_chan->descs_allocated = i;
- pd_chan->completed_cookie = chan->cookie = 1;
+ dma_cookie_init(chan);
spin_unlock_irq(&pd_chan->lock);
pdc_enable_irq(chan, 1);
@@ -578,19 +565,12 @@ static enum dma_status pd_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
struct pch_dma_chan *pd_chan = to_pd_chan(chan);
- dma_cookie_t last_used;
- dma_cookie_t last_completed;
- int ret;
+ enum dma_status ret;
spin_lock_irq(&pd_chan->lock);
- last_completed = pd_chan->completed_cookie;
- last_used = chan->cookie;
+ ret = dma_cookie_status(chan, cookie, txstate);
spin_unlock_irq(&pd_chan->lock);
- ret = dma_async_is_complete(cookie, last_completed, last_used);
-
- dma_set_tx_state(txstate, last_completed, last_used, 0);
-
return ret;
}
@@ -607,7 +587,8 @@ static void pd_issue_pending(struct dma_chan *chan)
static struct dma_async_tx_descriptor *pd_prep_slave_sg(struct dma_chan *chan,
struct scatterlist *sgl, unsigned int sg_len,
- enum dma_transfer_direction direction, unsigned long flags)
+ enum dma_transfer_direction direction, unsigned long flags,
+ void *context)
{
struct pch_dma_chan *pd_chan = to_pd_chan(chan);
struct pch_dma_slave *pd_slave = chan->private;
@@ -932,7 +913,7 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev,
struct pch_dma_chan *pd_chan = &pd->channels[i];
pd_chan->chan.device = &pd->dma;
- pd_chan->chan.cookie = 1;
+ dma_cookie_init(&pd_chan->chan);
pd_chan->membase = &regs->desc[i];
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 16b66c8..282caf1 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -1,4 +1,6 @@
-/* linux/drivers/dma/pl330.c
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
*
* Copyright (C) 2010 Samsung Electronics Co. Ltd.
* Jaswinder Singh <jassi.brar@samsung.com>
@@ -9,10 +11,15 @@
* (at your option) any later version.
*/
+#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/interrupt.h>
#include <linux/amba/bus.h>
@@ -21,8 +28,497 @@
#include <linux/scatterlist.h>
#include <linux/of.h>
+#include "dmaengine.h"
+#define PL330_MAX_CHAN 8
+#define PL330_MAX_IRQS 32
+#define PL330_MAX_PERI 32
+
+enum pl330_srccachectrl {
+ SCCTRL0, /* Noncacheable and nonbufferable */
+ SCCTRL1, /* Bufferable only */
+ SCCTRL2, /* Cacheable, but do not allocate */
+ SCCTRL3, /* Cacheable and bufferable, but do not allocate */
+ SINVALID1,
+ SINVALID2,
+ SCCTRL6, /* Cacheable write-through, allocate on reads only */
+ SCCTRL7, /* Cacheable write-back, allocate on reads only */
+};
+
+enum pl330_dstcachectrl {
+ DCCTRL0, /* Noncacheable and nonbufferable */
+ DCCTRL1, /* Bufferable only */
+ DCCTRL2, /* Cacheable, but do not allocate */
+ DCCTRL3, /* Cacheable and bufferable, but do not allocate */
+ DINVALID1, /* AWCACHE = 0x1000 */
+ DINVALID2,
+ DCCTRL6, /* Cacheable write-through, allocate on writes only */
+ DCCTRL7, /* Cacheable write-back, allocate on writes only */
+};
+
+enum pl330_byteswap {
+ SWAP_NO,
+ SWAP_2,
+ SWAP_4,
+ SWAP_8,
+ SWAP_16,
+};
+
+enum pl330_reqtype {
+ MEMTOMEM,
+ MEMTODEV,
+ DEVTOMEM,
+ DEVTODEV,
+};
+
+/* Register and Bit field Definitions */
+#define DS 0x0
+#define DS_ST_STOP 0x0
+#define DS_ST_EXEC 0x1
+#define DS_ST_CMISS 0x2
+#define DS_ST_UPDTPC 0x3
+#define DS_ST_WFE 0x4
+#define DS_ST_ATBRR 0x5
+#define DS_ST_QBUSY 0x6
+#define DS_ST_WFP 0x7
+#define DS_ST_KILL 0x8
+#define DS_ST_CMPLT 0x9
+#define DS_ST_FLTCMP 0xe
+#define DS_ST_FAULT 0xf
+
+#define DPC 0x4
+#define INTEN 0x20
+#define ES 0x24
+#define INTSTATUS 0x28
+#define INTCLR 0x2c
+#define FSM 0x30
+#define FSC 0x34
+#define FTM 0x38
+
+#define _FTC 0x40
+#define FTC(n) (_FTC + (n)*0x4)
+
+#define _CS 0x100
+#define CS(n) (_CS + (n)*0x8)
+#define CS_CNS (1 << 21)
+
+#define _CPC 0x104
+#define CPC(n) (_CPC + (n)*0x8)
+
+#define _SA 0x400
+#define SA(n) (_SA + (n)*0x20)
+
+#define _DA 0x404
+#define DA(n) (_DA + (n)*0x20)
+
+#define _CC 0x408
+#define CC(n) (_CC + (n)*0x20)
+
+#define CC_SRCINC (1 << 0)
+#define CC_DSTINC (1 << 14)
+#define CC_SRCPRI (1 << 8)
+#define CC_DSTPRI (1 << 22)
+#define CC_SRCNS (1 << 9)
+#define CC_DSTNS (1 << 23)
+#define CC_SRCIA (1 << 10)
+#define CC_DSTIA (1 << 24)
+#define CC_SRCBRSTLEN_SHFT 4
+#define CC_DSTBRSTLEN_SHFT 18
+#define CC_SRCBRSTSIZE_SHFT 1
+#define CC_DSTBRSTSIZE_SHFT 15
+#define CC_SRCCCTRL_SHFT 11
+#define CC_SRCCCTRL_MASK 0x7
+#define CC_DSTCCTRL_SHFT 25
+#define CC_DRCCCTRL_MASK 0x7
+#define CC_SWAP_SHFT 28
+
+#define _LC0 0x40c
+#define LC0(n) (_LC0 + (n)*0x20)
+
+#define _LC1 0x410
+#define LC1(n) (_LC1 + (n)*0x20)
+
+#define DBGSTATUS 0xd00
+#define DBG_BUSY (1 << 0)
+
+#define DBGCMD 0xd04
+#define DBGINST0 0xd08
+#define DBGINST1 0xd0c
+
+#define CR0 0xe00
+#define CR1 0xe04
+#define CR2 0xe08
+#define CR3 0xe0c
+#define CR4 0xe10
+#define CRD 0xe14
+
+#define PERIPH_ID 0xfe0
+#define PERIPH_REV_SHIFT 20
+#define PERIPH_REV_MASK 0xf
+#define PERIPH_REV_R0P0 0
+#define PERIPH_REV_R1P0 1
+#define PERIPH_REV_R1P1 2
+#define PCELL_ID 0xff0
+
+#define CR0_PERIPH_REQ_SET (1 << 0)
+#define CR0_BOOT_EN_SET (1 << 1)
+#define CR0_BOOT_MAN_NS (1 << 2)
+#define CR0_NUM_CHANS_SHIFT 4
+#define CR0_NUM_CHANS_MASK 0x7
+#define CR0_NUM_PERIPH_SHIFT 12
+#define CR0_NUM_PERIPH_MASK 0x1f
+#define CR0_NUM_EVENTS_SHIFT 17
+#define CR0_NUM_EVENTS_MASK 0x1f
+
+#define CR1_ICACHE_LEN_SHIFT 0
+#define CR1_ICACHE_LEN_MASK 0x7
+#define CR1_NUM_ICACHELINES_SHIFT 4
+#define CR1_NUM_ICACHELINES_MASK 0xf
+
+#define CRD_DATA_WIDTH_SHIFT 0
+#define CRD_DATA_WIDTH_MASK 0x7
+#define CRD_WR_CAP_SHIFT 4
+#define CRD_WR_CAP_MASK 0x7
+#define CRD_WR_Q_DEP_SHIFT 8
+#define CRD_WR_Q_DEP_MASK 0xf
+#define CRD_RD_CAP_SHIFT 12
+#define CRD_RD_CAP_MASK 0x7
+#define CRD_RD_Q_DEP_SHIFT 16
+#define CRD_RD_Q_DEP_MASK 0xf
+#define CRD_DATA_BUFF_SHIFT 20
+#define CRD_DATA_BUFF_MASK 0x3ff
+
+#define PART 0x330
+#define DESIGNER 0x41
+#define REVISION 0x0
+#define INTEG_CFG 0x0
+#define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12))
+
+#define PCELL_ID_VAL 0xb105f00d
+
+#define PL330_STATE_STOPPED (1 << 0)
+#define PL330_STATE_EXECUTING (1 << 1)
+#define PL330_STATE_WFE (1 << 2)
+#define PL330_STATE_FAULTING (1 << 3)
+#define PL330_STATE_COMPLETING (1 << 4)
+#define PL330_STATE_WFP (1 << 5)
+#define PL330_STATE_KILLING (1 << 6)
+#define PL330_STATE_FAULT_COMPLETING (1 << 7)
+#define PL330_STATE_CACHEMISS (1 << 8)
+#define PL330_STATE_UPDTPC (1 << 9)
+#define PL330_STATE_ATBARRIER (1 << 10)
+#define PL330_STATE_QUEUEBUSY (1 << 11)
+#define PL330_STATE_INVALID (1 << 15)
+
+#define PL330_STABLE_STATES (PL330_STATE_STOPPED | PL330_STATE_EXECUTING \
+ | PL330_STATE_WFE | PL330_STATE_FAULTING)
+
+#define CMD_DMAADDH 0x54
+#define CMD_DMAEND 0x00
+#define CMD_DMAFLUSHP 0x35
+#define CMD_DMAGO 0xa0
+#define CMD_DMALD 0x04
+#define CMD_DMALDP 0x25
+#define CMD_DMALP 0x20
+#define CMD_DMALPEND 0x28
+#define CMD_DMAKILL 0x01
+#define CMD_DMAMOV 0xbc
+#define CMD_DMANOP 0x18
+#define CMD_DMARMB 0x12
+#define CMD_DMASEV 0x34
+#define CMD_DMAST 0x08
+#define CMD_DMASTP 0x29
+#define CMD_DMASTZ 0x0c
+#define CMD_DMAWFE 0x36
+#define CMD_DMAWFP 0x30
+#define CMD_DMAWMB 0x13
+
+#define SZ_DMAADDH 3
+#define SZ_DMAEND 1
+#define SZ_DMAFLUSHP 2
+#define SZ_DMALD 1
+#define SZ_DMALDP 2
+#define SZ_DMALP 2
+#define SZ_DMALPEND 2
+#define SZ_DMAKILL 1
+#define SZ_DMAMOV 6
+#define SZ_DMANOP 1
+#define SZ_DMARMB 1
+#define SZ_DMASEV 2
+#define SZ_DMAST 1
+#define SZ_DMASTP 2
+#define SZ_DMASTZ 1
+#define SZ_DMAWFE 2
+#define SZ_DMAWFP 2
+#define SZ_DMAWMB 1
+#define SZ_DMAGO 6
+
+#define BRST_LEN(ccr) ((((ccr) >> CC_SRCBRSTLEN_SHFT) & 0xf) + 1)
+#define BRST_SIZE(ccr) (1 << (((ccr) >> CC_SRCBRSTSIZE_SHFT) & 0x7))
+
+#define BYTE_TO_BURST(b, ccr) ((b) / BRST_SIZE(ccr) / BRST_LEN(ccr))
+#define BURST_TO_BYTE(c, ccr) ((c) * BRST_SIZE(ccr) * BRST_LEN(ccr))
+
+/*
+ * With 256 bytes, we can do more than 2.5MB and 5MB xfers per req
+ * at 1byte/burst for P<->M and M<->M respectively.
+ * For typical scenario, at 1word/burst, 10MB and 20MB xfers per req
+ * should be enough for P<->M and M<->M respectively.
+ */
+#define MCODE_BUFF_PER_REQ 256
+
+/* If the _pl330_req is available to the client */
+#define IS_FREE(req) (*((u8 *)((req)->mc_cpu)) == CMD_DMAEND)
+
+/* Use this _only_ to wait on transient states */
+#define UNTIL(t, s) while (!(_state(t) & (s))) cpu_relax();
+
+#ifdef PL330_DEBUG_MCGEN
+static unsigned cmd_line;
+#define PL330_DBGCMD_DUMP(off, x...) do { \
+ printk("%x:", cmd_line); \
+ printk(x); \
+ cmd_line += off; \
+ } while (0)
+#define PL330_DBGMC_START(addr) (cmd_line = addr)
+#else
+#define PL330_DBGCMD_DUMP(off, x...) do {} while (0)
+#define PL330_DBGMC_START(addr) do {} while (0)
+#endif
+
+/* The number of default descriptors */
+
#define NR_DEFAULT_DESC 16
+/* Populated by the PL330 core driver for DMA API driver's info */
+struct pl330_config {
+ u32 periph_id;
+ u32 pcell_id;
+#define DMAC_MODE_NS (1 << 0)
+ unsigned int mode;
+ unsigned int data_bus_width:10; /* In number of bits */
+ unsigned int data_buf_dep:10;
+ unsigned int num_chan:4;
+ unsigned int num_peri:6;
+ u32 peri_ns;
+ unsigned int num_events:6;
+ u32 irq_ns;
+};
+
+/* Handle to the DMAC provided to the PL330 core */
+struct pl330_info {
+ /* Owning device */
+ struct device *dev;
+ /* Size of MicroCode buffers for each channel. */
+ unsigned mcbufsz;
+ /* ioremap'ed address of PL330 registers. */
+ void __iomem *base;
+ /* Client can freely use it. */
+ void *client_data;
+ /* PL330 core data, Client must not touch it. */
+ void *pl330_data;
+ /* Populated by the PL330 core driver during pl330_add */
+ struct pl330_config pcfg;
+ /*
+ * If the DMAC has some reset mechanism, then the
+ * client may want to provide pointer to the method.
+ */
+ void (*dmac_reset)(struct pl330_info *pi);
+};
+
+/**
+ * Request Configuration.
+ * The PL330 core does not modify this and uses the last
+ * working configuration if the request doesn't provide any.
+ *
+ * The Client may want to provide this info only for the
+ * first request and a request with new settings.
+ */
+struct pl330_reqcfg {
+ /* Address Incrementing */
+ unsigned dst_inc:1;
+ unsigned src_inc:1;
+
+ /*
+ * For now, the SRC & DST protection levels
+ * and burst size/length are assumed same.
+ */
+ bool nonsecure;
+ bool privileged;
+ bool insnaccess;
+ unsigned brst_len:5;
+ unsigned brst_size:3; /* in power of 2 */
+
+ enum pl330_dstcachectrl dcctl;
+ enum pl330_srccachectrl scctl;
+ enum pl330_byteswap swap;
+ struct pl330_config *pcfg;
+};
+
+/*
+ * One cycle of DMAC operation.
+ * There may be more than one xfer in a request.
+ */
+struct pl330_xfer {
+ u32 src_addr;
+ u32 dst_addr;
+ /* Size to xfer */
+ u32 bytes;
+ /*
+ * Pointer to next xfer in the list.
+ * The last xfer in the req must point to NULL.
+ */
+ struct pl330_xfer *next;
+};
+
+/* The xfer callbacks are made with one of these arguments. */
+enum pl330_op_err {
+ /* The all xfers in the request were success. */
+ PL330_ERR_NONE,
+ /* If req aborted due to global error. */
+ PL330_ERR_ABORT,
+ /* If req failed due to problem with Channel. */
+ PL330_ERR_FAIL,
+};
+
+/* A request defining Scatter-Gather List ending with NULL xfer. */
+struct pl330_req {
+ enum pl330_reqtype rqtype;
+ /* Index of peripheral for the xfer. */
+ unsigned peri:5;
+ /* Unique token for this xfer, set by the client. */
+ void *token;
+ /* Callback to be called after xfer. */
+ void (*xfer_cb)(void *token, enum pl330_op_err err);
+ /* If NULL, req will be done at last set parameters. */
+ struct pl330_reqcfg *cfg;
+ /* Pointer to first xfer in the request. */
+ struct pl330_xfer *x;
+};
+
+/*
+ * To know the status of the channel and DMAC, the client
+ * provides a pointer to this structure. The PL330 core
+ * fills it with current information.
+ */
+struct pl330_chanstatus {
+ /*
+ * If the DMAC engine halted due to some error,
+ * the client should remove-add DMAC.
+ */
+ bool dmac_halted;
+ /*
+ * If channel is halted due to some error,
+ * the client should ABORT/FLUSH and START the channel.
+ */
+ bool faulting;
+ /* Location of last load */
+ u32 src_addr;
+ /* Location of last store */
+ u32 dst_addr;
+ /*
+ * Pointer to the currently active req, NULL if channel is
+ * inactive, even though the requests may be present.
+ */
+ struct pl330_req *top_req;
+ /* Pointer to req waiting second in the queue if any. */
+ struct pl330_req *wait_req;
+};
+
+enum pl330_chan_op {
+ /* Start the channel */
+ PL330_OP_START,
+ /* Abort the active xfer */
+ PL330_OP_ABORT,
+ /* Stop xfer and flush queue */
+ PL330_OP_FLUSH,
+};
+
+struct _xfer_spec {
+ u32 ccr;
+ struct pl330_req *r;
+ struct pl330_xfer *x;
+};
+
+enum dmamov_dst {
+ SAR = 0,
+ CCR,
+ DAR,
+};
+
+enum pl330_dst {
+ SRC = 0,
+ DST,
+};
+
+enum pl330_cond {
+ SINGLE,
+ BURST,
+ ALWAYS,
+};
+
+struct _pl330_req {
+ u32 mc_bus;
+ void *mc_cpu;
+ /* Number of bytes taken to setup MC for the req */
+ u32 mc_len;
+ struct pl330_req *r;
+ /* Hook to attach to DMAC's list of reqs with due callback */
+ struct list_head rqd;
+};
+
+/* ToBeDone for tasklet */
+struct _pl330_tbd {
+ bool reset_dmac;
+ bool reset_mngr;
+ u8 reset_chan;
+};
+
+/* A DMAC Thread */
+struct pl330_thread {
+ u8 id;
+ int ev;
+ /* If the channel is not yet acquired by any client */
+ bool free;
+ /* Parent DMAC */
+ struct pl330_dmac *dmac;
+ /* Only two at a time */
+ struct _pl330_req req[2];
+ /* Index of the last enqueued request */
+ unsigned lstenq;
+ /* Index of the last submitted request or -1 if the DMA is stopped */
+ int req_running;
+};
+
+enum pl330_dmac_state {
+ UNINIT,
+ INIT,
+ DYING,
+};
+
+/* A DMAC */
+struct pl330_dmac {
+ spinlock_t lock;
+ /* Holds list of reqs with due callbacks */
+ struct list_head req_done;
+ /* Pointer to platform specific stuff */
+ struct pl330_info *pinfo;
+ /* Maximum possible events/irqs */
+ int events[32];
+ /* BUS address of MicroCode buffer */
+ u32 mcode_bus;
+ /* CPU address of MicroCode buffer */
+ void *mcode_cpu;
+ /* List of all Channel threads */
+ struct pl330_thread *channels;
+ /* Pointer to the MANAGER thread */
+ struct pl330_thread *manager;
+ /* To handle bad news in interrupt */
+ struct tasklet_struct tasks;
+ struct _pl330_tbd dmac_tbd;
+ /* State of DMAC operation */
+ enum pl330_dmac_state state;
+};
+
enum desc_status {
/* In the DMAC pool */
FREE,
@@ -51,9 +547,6 @@ struct dma_pl330_chan {
/* DMA-Engine Channel */
struct dma_chan chan;
- /* Last completed cookie */
- dma_cookie_t completed;
-
/* List of to be xfered descriptors */
struct list_head work_list;
@@ -117,6 +610,1599 @@ struct dma_pl330_desc {
struct dma_pl330_chan *pchan;
};
+static inline void _callback(struct pl330_req *r, enum pl330_op_err err)
+{
+ if (r && r->xfer_cb)
+ r->xfer_cb(r->token, err);
+}
+
+static inline bool _queue_empty(struct pl330_thread *thrd)
+{
+ return (IS_FREE(&thrd->req[0]) && IS_FREE(&thrd->req[1]))
+ ? true : false;
+}
+
+static inline bool _queue_full(struct pl330_thread *thrd)
+{
+ return (IS_FREE(&thrd->req[0]) || IS_FREE(&thrd->req[1]))
+ ? false : true;
+}
+
+static inline bool is_manager(struct pl330_thread *thrd)
+{
+ struct pl330_dmac *pl330 = thrd->dmac;
+
+ /* MANAGER is indexed at the end */
+ if (thrd->id == pl330->pinfo->pcfg.num_chan)
+ return true;
+ else
+ return false;
+}
+
+/* If manager of the thread is in Non-Secure mode */
+static inline bool _manager_ns(struct pl330_thread *thrd)
+{
+ struct pl330_dmac *pl330 = thrd->dmac;
+
+ return (pl330->pinfo->pcfg.mode & DMAC_MODE_NS) ? true : false;
+}
+
+static inline u32 get_id(struct pl330_info *pi, u32 off)
+{
+ void __iomem *regs = pi->base;
+ u32 id = 0;
+
+ id |= (readb(regs + off + 0x0) << 0);
+ id |= (readb(regs + off + 0x4) << 8);
+ id |= (readb(regs + off + 0x8) << 16);
+ id |= (readb(regs + off + 0xc) << 24);
+
+ return id;
+}
+
+static inline u32 get_revision(u32 periph_id)
+{
+ return (periph_id >> PERIPH_REV_SHIFT) & PERIPH_REV_MASK;
+}
+
+static inline u32 _emit_ADDH(unsigned dry_run, u8 buf[],
+ enum pl330_dst da, u16 val)
+{
+ if (dry_run)
+ return SZ_DMAADDH;
+
+ buf[0] = CMD_DMAADDH;
+ buf[0] |= (da << 1);
+ *((u16 *)&buf[1]) = val;
+
+ PL330_DBGCMD_DUMP(SZ_DMAADDH, "\tDMAADDH %s %u\n",
+ da == 1 ? "DA" : "SA", val);
+
+ return SZ_DMAADDH;
+}
+
+static inline u32 _emit_END(unsigned dry_run, u8 buf[])
+{
+ if (dry_run)
+ return SZ_DMAEND;
+
+ buf[0] = CMD_DMAEND;
+
+ PL330_DBGCMD_DUMP(SZ_DMAEND, "\tDMAEND\n");
+
+ return SZ_DMAEND;
+}
+
+static inline u32 _emit_FLUSHP(unsigned dry_run, u8 buf[], u8 peri)
+{
+ if (dry_run)
+ return SZ_DMAFLUSHP;
+
+ buf[0] = CMD_DMAFLUSHP;
+
+ peri &= 0x1f;
+ peri <<= 3;
+ buf[1] = peri;
+
+ PL330_DBGCMD_DUMP(SZ_DMAFLUSHP, "\tDMAFLUSHP %u\n", peri >> 3);
+
+ return SZ_DMAFLUSHP;
+}
+
+static inline u32 _emit_LD(unsigned dry_run, u8 buf[], enum pl330_cond cond)
+{
+ if (dry_run)
+ return SZ_DMALD;
+
+ buf[0] = CMD_DMALD;
+
+ if (cond == SINGLE)
+ buf[0] |= (0 << 1) | (1 << 0);
+ else if (cond == BURST)
+ buf[0] |= (1 << 1) | (1 << 0);
+
+ PL330_DBGCMD_DUMP(SZ_DMALD, "\tDMALD%c\n",
+ cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A'));
+
+ return SZ_DMALD;
+}
+
+static inline u32 _emit_LDP(unsigned dry_run, u8 buf[],
+ enum pl330_cond cond, u8 peri)
+{
+ if (dry_run)
+ return SZ_DMALDP;
+
+ buf[0] = CMD_DMALDP;
+
+ if (cond == BURST)
+ buf[0] |= (1 << 1);
+
+ peri &= 0x1f;
+ peri <<= 3;
+ buf[1] = peri;
+
+ PL330_DBGCMD_DUMP(SZ_DMALDP, "\tDMALDP%c %u\n",
+ cond == SINGLE ? 'S' : 'B', peri >> 3);
+
+ return SZ_DMALDP;
+}
+
+static inline u32 _emit_LP(unsigned dry_run, u8 buf[],
+ unsigned loop, u8 cnt)
+{
+ if (dry_run)
+ return SZ_DMALP;
+
+ buf[0] = CMD_DMALP;
+
+ if (loop)
+ buf[0] |= (1 << 1);
+
+ cnt--; /* DMAC increments by 1 internally */
+ buf[1] = cnt;
+
+ PL330_DBGCMD_DUMP(SZ_DMALP, "\tDMALP_%c %u\n", loop ? '1' : '0', cnt);
+
+ return SZ_DMALP;
+}
+
+struct _arg_LPEND {
+ enum pl330_cond cond;
+ bool forever;
+ unsigned loop;
+ u8 bjump;
+};
+
+static inline u32 _emit_LPEND(unsigned dry_run, u8 buf[],
+ const struct _arg_LPEND *arg)
+{
+ enum pl330_cond cond = arg->cond;
+ bool forever = arg->forever;
+ unsigned loop = arg->loop;
+ u8 bjump = arg->bjump;
+
+ if (dry_run)
+ return SZ_DMALPEND;
+
+ buf[0] = CMD_DMALPEND;
+
+ if (loop)
+ buf[0] |= (1 << 2);
+
+ if (!forever)
+ buf[0] |= (1 << 4);
+
+ if (cond == SINGLE)
+ buf[0] |= (0 << 1) | (1 << 0);
+ else if (cond == BURST)
+ buf[0] |= (1 << 1) | (1 << 0);
+
+ buf[1] = bjump;
+
+ PL330_DBGCMD_DUMP(SZ_DMALPEND, "\tDMALP%s%c_%c bjmpto_%x\n",
+ forever ? "FE" : "END",
+ cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A'),
+ loop ? '1' : '0',
+ bjump);
+
+ return SZ_DMALPEND;
+}
+
+static inline u32 _emit_KILL(unsigned dry_run, u8 buf[])
+{
+ if (dry_run)
+ return SZ_DMAKILL;
+
+ buf[0] = CMD_DMAKILL;
+
+ return SZ_DMAKILL;
+}
+
+static inline u32 _emit_MOV(unsigned dry_run, u8 buf[],
+ enum dmamov_dst dst, u32 val)
+{
+ if (dry_run)
+ return SZ_DMAMOV;
+
+ buf[0] = CMD_DMAMOV;
+ buf[1] = dst;
+ *((u32 *)&buf[2]) = val;
+
+ PL330_DBGCMD_DUMP(SZ_DMAMOV, "\tDMAMOV %s 0x%x\n",
+ dst == SAR ? "SAR" : (dst == DAR ? "DAR" : "CCR"), val);
+
+ return SZ_DMAMOV;
+}
+
+static inline u32 _emit_NOP(unsigned dry_run, u8 buf[])
+{
+ if (dry_run)
+ return SZ_DMANOP;
+
+ buf[0] = CMD_DMANOP;
+
+ PL330_DBGCMD_DUMP(SZ_DMANOP, "\tDMANOP\n");
+
+ return SZ_DMANOP;
+}
+
+static inline u32 _emit_RMB(unsigned dry_run, u8 buf[])
+{
+ if (dry_run)
+ return SZ_DMARMB;
+
+ buf[0] = CMD_DMARMB;
+
+ PL330_DBGCMD_DUMP(SZ_DMARMB, "\tDMARMB\n");
+
+ return SZ_DMARMB;
+}
+
+static inline u32 _emit_SEV(unsigned dry_run, u8 buf[], u8 ev)
+{
+ if (dry_run)
+ return SZ_DMASEV;
+
+ buf[0] = CMD_DMASEV;
+
+ ev &= 0x1f;
+ ev <<= 3;
+ buf[1] = ev;
+
+ PL330_DBGCMD_DUMP(SZ_DMASEV, "\tDMASEV %u\n", ev >> 3);
+
+ return SZ_DMASEV;
+}
+
+static inline u32 _emit_ST(unsigned dry_run, u8 buf[], enum pl330_cond cond)
+{
+ if (dry_run)
+ return SZ_DMAST;
+
+ buf[0] = CMD_DMAST;
+
+ if (cond == SINGLE)
+ buf[0] |= (0 << 1) | (1 << 0);
+ else if (cond == BURST)
+ buf[0] |= (1 << 1) | (1 << 0);
+
+ PL330_DBGCMD_DUMP(SZ_DMAST, "\tDMAST%c\n",
+ cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A'));
+
+ return SZ_DMAST;
+}
+
+static inline u32 _emit_STP(unsigned dry_run, u8 buf[],
+ enum pl330_cond cond, u8 peri)
+{
+ if (dry_run)
+ return SZ_DMASTP;
+
+ buf[0] = CMD_DMASTP;
+
+ if (cond == BURST)
+ buf[0] |= (1 << 1);
+
+ peri &= 0x1f;
+ peri <<= 3;
+ buf[1] = peri;
+
+ PL330_DBGCMD_DUMP(SZ_DMASTP, "\tDMASTP%c %u\n",
+ cond == SINGLE ? 'S' : 'B', peri >> 3);
+
+ return SZ_DMASTP;
+}
+
+static inline u32 _emit_STZ(unsigned dry_run, u8 buf[])
+{
+ if (dry_run)
+ return SZ_DMASTZ;
+
+ buf[0] = CMD_DMASTZ;
+
+ PL330_DBGCMD_DUMP(SZ_DMASTZ, "\tDMASTZ\n");
+
+ return SZ_DMASTZ;
+}
+
+static inline u32 _emit_WFE(unsigned dry_run, u8 buf[], u8 ev,
+ unsigned invalidate)
+{
+ if (dry_run)
+ return SZ_DMAWFE;
+
+ buf[0] = CMD_DMAWFE;
+
+ ev &= 0x1f;
+ ev <<= 3;
+ buf[1] = ev;
+
+ if (invalidate)
+ buf[1] |= (1 << 1);
+
+ PL330_DBGCMD_DUMP(SZ_DMAWFE, "\tDMAWFE %u%s\n",
+ ev >> 3, invalidate ? ", I" : "");
+
+ return SZ_DMAWFE;
+}
+
+static inline u32 _emit_WFP(unsigned dry_run, u8 buf[],
+ enum pl330_cond cond, u8 peri)
+{
+ if (dry_run)
+ return SZ_DMAWFP;
+
+ buf[0] = CMD_DMAWFP;
+
+ if (cond == SINGLE)
+ buf[0] |= (0 << 1) | (0 << 0);
+ else if (cond == BURST)
+ buf[0] |= (1 << 1) | (0 << 0);
+ else
+ buf[0] |= (0 << 1) | (1 << 0);
+
+ peri &= 0x1f;
+ peri <<= 3;
+ buf[1] = peri;
+
+ PL330_DBGCMD_DUMP(SZ_DMAWFP, "\tDMAWFP%c %u\n",
+ cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'P'), peri >> 3);
+
+ return SZ_DMAWFP;
+}
+
+static inline u32 _emit_WMB(unsigned dry_run, u8 buf[])
+{
+ if (dry_run)
+ return SZ_DMAWMB;
+
+ buf[0] = CMD_DMAWMB;
+
+ PL330_DBGCMD_DUMP(SZ_DMAWMB, "\tDMAWMB\n");
+
+ return SZ_DMAWMB;
+}
+
+struct _arg_GO {
+ u8 chan;
+ u32 addr;
+ unsigned ns;
+};
+
+static inline u32 _emit_GO(unsigned dry_run, u8 buf[],
+ const struct _arg_GO *arg)
+{
+ u8 chan = arg->chan;
+ u32 addr = arg->addr;
+ unsigned ns = arg->ns;
+
+ if (dry_run)
+ return SZ_DMAGO;
+
+ buf[0] = CMD_DMAGO;
+ buf[0] |= (ns << 1);
+
+ buf[1] = chan & 0x7;
+
+ *((u32 *)&buf[2]) = addr;
+
+ return SZ_DMAGO;
+}
+
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+
+/* Returns Time-Out */
+static bool _until_dmac_idle(struct pl330_thread *thrd)
+{
+ void __iomem *regs = thrd->dmac->pinfo->base;
+ unsigned long loops = msecs_to_loops(5);
+
+ do {
+ /* Until Manager is Idle */
+ if (!(readl(regs + DBGSTATUS) & DBG_BUSY))
+ break;
+
+ cpu_relax();
+ } while (--loops);
+
+ if (!loops)
+ return true;
+
+ return false;
+}
+
+static inline void _execute_DBGINSN(struct pl330_thread *thrd,
+ u8 insn[], bool as_manager)
+{
+ void __iomem *regs = thrd->dmac->pinfo->base;
+ u32 val;
+
+ val = (insn[0] << 16) | (insn[1] << 24);
+ if (!as_manager) {
+ val |= (1 << 0);
+ val |= (thrd->id << 8); /* Channel Number */
+ }
+ writel(val, regs + DBGINST0);
+
+ val = *((u32 *)&insn[2]);
+ writel(val, regs + DBGINST1);
+
+ /* If timed out due to halted state-machine */
+ if (_until_dmac_idle(thrd)) {
+ dev_err(thrd->dmac->pinfo->dev, "DMAC halted!\n");
+ return;
+ }
+
+ /* Get going */
+ writel(0, regs + DBGCMD);
+}
+
+/*
+ * Mark a _pl330_req as free.
+ * We do it by writing DMAEND as the first instruction
+ * because no valid request is going to have DMAEND as
+ * its first instruction to execute.
+ */
+static void mark_free(struct pl330_thread *thrd, int idx)
+{
+ struct _pl330_req *req = &thrd->req[idx];
+
+ _emit_END(0, req->mc_cpu);
+ req->mc_len = 0;
+
+ thrd->req_running = -1;
+}
+
+static inline u32 _state(struct pl330_thread *thrd)
+{
+ void __iomem *regs = thrd->dmac->pinfo->base;
+ u32 val;
+
+ if (is_manager(thrd))
+ val = readl(regs + DS) & 0xf;
+ else
+ val = readl(regs + CS(thrd->id)) & 0xf;
+
+ switch (val) {
+ case DS_ST_STOP:
+ return PL330_STATE_STOPPED;
+ case DS_ST_EXEC:
+ return PL330_STATE_EXECUTING;
+ case DS_ST_CMISS:
+ return PL330_STATE_CACHEMISS;
+ case DS_ST_UPDTPC:
+ return PL330_STATE_UPDTPC;
+ case DS_ST_WFE:
+ return PL330_STATE_WFE;
+ case DS_ST_FAULT:
+ return PL330_STATE_FAULTING;
+ case DS_ST_ATBRR:
+ if (is_manager(thrd))
+ return PL330_STATE_INVALID;
+ else
+ return PL330_STATE_ATBARRIER;
+ case DS_ST_QBUSY:
+ if (is_manager(thrd))
+ return PL330_STATE_INVALID;
+ else
+ return PL330_STATE_QUEUEBUSY;
+ case DS_ST_WFP:
+ if (is_manager(thrd))
+ return PL330_STATE_INVALID;
+ else
+ return PL330_STATE_WFP;
+ case DS_ST_KILL:
+ if (is_manager(thrd))
+ return PL330_STATE_INVALID;
+ else
+ return PL330_STATE_KILLING;
+ case DS_ST_CMPLT:
+ if (is_manager(thrd))
+ return PL330_STATE_INVALID;
+ else
+ return PL330_STATE_COMPLETING;
+ case DS_ST_FLTCMP:
+ if (is_manager(thrd))
+ return PL330_STATE_INVALID;
+ else
+ return PL330_STATE_FAULT_COMPLETING;
+ default:
+ return PL330_STATE_INVALID;
+ }
+}
+
+static void _stop(struct pl330_thread *thrd)
+{
+ void __iomem *regs = thrd->dmac->pinfo->base;
+ u8 insn[6] = {0, 0, 0, 0, 0, 0};
+
+ if (_state(thrd) == PL330_STATE_FAULT_COMPLETING)
+ UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING);
+
+ /* Return if nothing needs to be done */
+ if (_state(thrd) == PL330_STATE_COMPLETING
+ || _state(thrd) == PL330_STATE_KILLING
+ || _state(thrd) == PL330_STATE_STOPPED)
+ return;
+
+ _emit_KILL(0, insn);
+
+ /* Stop generating interrupts for SEV */
+ writel(readl(regs + INTEN) & ~(1 << thrd->ev), regs + INTEN);
+
+ _execute_DBGINSN(thrd, insn, is_manager(thrd));
+}
+
+/* Start doing req 'idx' of thread 'thrd' */
+static bool _trigger(struct pl330_thread *thrd)
+{
+ void __iomem *regs = thrd->dmac->pinfo->base;
+ struct _pl330_req *req;
+ struct pl330_req *r;
+ struct _arg_GO go;
+ unsigned ns;
+ u8 insn[6] = {0, 0, 0, 0, 0, 0};
+ int idx;
+
+ /* Return if already ACTIVE */
+ if (_state(thrd) != PL330_STATE_STOPPED)
+ return true;
+
+ idx = 1 - thrd->lstenq;
+ if (!IS_FREE(&thrd->req[idx]))
+ req = &thrd->req[idx];
+ else {
+ idx = thrd->lstenq;
+ if (!IS_FREE(&thrd->req[idx]))
+ req = &thrd->req[idx];
+ else
+ req = NULL;
+ }
+
+ /* Return if no request */
+ if (!req || !req->r)
+ return true;
+
+ r = req->r;
+
+ if (r->cfg)
+ ns = r->cfg->nonsecure ? 1 : 0;
+ else if (readl(regs + CS(thrd->id)) & CS_CNS)
+ ns = 1;
+ else
+ ns = 0;
+
+ /* See 'Abort Sources' point-4 at Page 2-25 */
+ if (_manager_ns(thrd) && !ns)
+ dev_info(thrd->dmac->pinfo->dev, "%s:%d Recipe for ABORT!\n",
+ __func__, __LINE__);
+
+ go.chan = thrd->id;
+ go.addr = req->mc_bus;
+ go.ns = ns;
+ _emit_GO(0, insn, &go);
+
+ /* Set to generate interrupts for SEV */
+ writel(readl(regs + INTEN) | (1 << thrd->ev), regs + INTEN);
+
+ /* Only manager can execute GO */
+ _execute_DBGINSN(thrd, insn, true);
+
+ thrd->req_running = idx;
+
+ return true;
+}
+
+static bool _start(struct pl330_thread *thrd)
+{
+ switch (_state(thrd)) {
+ case PL330_STATE_FAULT_COMPLETING:
+ UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING);
+
+ if (_state(thrd) == PL330_STATE_KILLING)
+ UNTIL(thrd, PL330_STATE_STOPPED)
+
+ case PL330_STATE_FAULTING:
+ _stop(thrd);
+
+ case PL330_STATE_KILLING:
+ case PL330_STATE_COMPLETING:
+ UNTIL(thrd, PL330_STATE_STOPPED)
+
+ case PL330_STATE_STOPPED:
+ return _trigger(thrd);
+
+ case PL330_STATE_WFP:
+ case PL330_STATE_QUEUEBUSY:
+ case PL330_STATE_ATBARRIER:
+ case PL330_STATE_UPDTPC:
+ case PL330_STATE_CACHEMISS:
+ case PL330_STATE_EXECUTING:
+ return true;
+
+ case PL330_STATE_WFE: /* For RESUME, nothing yet */
+ default:
+ return false;
+ }
+}
+
+static inline int _ldst_memtomem(unsigned dry_run, u8 buf[],
+ const struct _xfer_spec *pxs, int cyc)
+{
+ int off = 0;
+ struct pl330_config *pcfg = pxs->r->cfg->pcfg;
+
+ /* check lock-up free version */
+ if (get_revision(pcfg->periph_id) >= PERIPH_REV_R1P0) {
+ while (cyc--) {
+ off += _emit_LD(dry_run, &buf[off], ALWAYS);
+ off += _emit_ST(dry_run, &buf[off], ALWAYS);
+ }
+ } else {
+ while (cyc--) {
+ off += _emit_LD(dry_run, &buf[off], ALWAYS);
+ off += _emit_RMB(dry_run, &buf[off]);
+ off += _emit_ST(dry_run, &buf[off], ALWAYS);
+ off += _emit_WMB(dry_run, &buf[off]);
+ }
+ }
+
+ return off;
+}
+
+static inline int _ldst_devtomem(unsigned dry_run, u8 buf[],
+ const struct _xfer_spec *pxs, int cyc)
+{
+ int off = 0;
+
+ while (cyc--) {
+ off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->r->peri);
+ off += _emit_LDP(dry_run, &buf[off], SINGLE, pxs->r->peri);
+ off += _emit_ST(dry_run, &buf[off], ALWAYS);
+ off += _emit_FLUSHP(dry_run, &buf[off], pxs->r->peri);
+ }
+
+ return off;
+}
+
+static inline int _ldst_memtodev(unsigned dry_run, u8 buf[],
+ const struct _xfer_spec *pxs, int cyc)
+{
+ int off = 0;
+
+ while (cyc--) {
+ off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->r->peri);
+ off += _emit_LD(dry_run, &buf[off], ALWAYS);
+ off += _emit_STP(dry_run, &buf[off], SINGLE, pxs->r->peri);
+ off += _emit_FLUSHP(dry_run, &buf[off], pxs->r->peri);
+ }
+
+ return off;
+}
+
+static int _bursts(unsigned dry_run, u8 buf[],
+ const struct _xfer_spec *pxs, int cyc)
+{
+ int off = 0;
+
+ switch (pxs->r->rqtype) {
+ case MEMTODEV:
+ off += _ldst_memtodev(dry_run, &buf[off], pxs, cyc);
+ break;
+ case DEVTOMEM:
+ off += _ldst_devtomem(dry_run, &buf[off], pxs, cyc);
+ break;
+ case MEMTOMEM:
+ off += _ldst_memtomem(dry_run, &buf[off], pxs, cyc);
+ break;
+ default:
+ off += 0x40000000; /* Scare off the Client */
+ break;
+ }
+
+ return off;
+}
+
+/* Returns bytes consumed and updates bursts */
+static inline int _loop(unsigned dry_run, u8 buf[],
+ unsigned long *bursts, const struct _xfer_spec *pxs)
+{
+ int cyc, cycmax, szlp, szlpend, szbrst, off;
+ unsigned lcnt0, lcnt1, ljmp0, ljmp1;
+ struct _arg_LPEND lpend;
+
+ /* Max iterations possible in DMALP is 256 */
+ if (*bursts >= 256*256) {
+ lcnt1 = 256;
+ lcnt0 = 256;
+ cyc = *bursts / lcnt1 / lcnt0;
+ } else if (*bursts > 256) {
+ lcnt1 = 256;
+ lcnt0 = *bursts / lcnt1;
+ cyc = 1;
+ } else {
+ lcnt1 = *bursts;
+ lcnt0 = 0;
+ cyc = 1;
+ }
+
+ szlp = _emit_LP(1, buf, 0, 0);
+ szbrst = _bursts(1, buf, pxs, 1);
+
+ lpend.cond = ALWAYS;
+ lpend.forever = false;
+ lpend.loop = 0;
+ lpend.bjump = 0;
+ szlpend = _emit_LPEND(1, buf, &lpend);
+
+ if (lcnt0) {
+ szlp *= 2;
+ szlpend *= 2;
+ }
+
+ /*
+ * Max bursts that we can unroll due to limit on the
+ * size of backward jump that can be encoded in DMALPEND
+ * which is 8-bits and hence 255
+ */
+ cycmax = (255 - (szlp + szlpend)) / szbrst;
+
+ cyc = (cycmax < cyc) ? cycmax : cyc;
+
+ off = 0;
+
+ if (lcnt0) {
+ off += _emit_LP(dry_run, &buf[off], 0, lcnt0);
+ ljmp0 = off;
+ }
+
+ off += _emit_LP(dry_run, &buf[off], 1, lcnt1);
+ ljmp1 = off;
+
+ off += _bursts(dry_run, &buf[off], pxs, cyc);
+
+ lpend.cond = ALWAYS;
+ lpend.forever = false;
+ lpend.loop = 1;
+ lpend.bjump = off - ljmp1;
+ off += _emit_LPEND(dry_run, &buf[off], &lpend);
+
+ if (lcnt0) {
+ lpend.cond = ALWAYS;
+ lpend.forever = false;
+ lpend.loop = 0;
+ lpend.bjump = off - ljmp0;
+ off += _emit_LPEND(dry_run, &buf[off], &lpend);
+ }
+
+ *bursts = lcnt1 * cyc;
+ if (lcnt0)
+ *bursts *= lcnt0;
+
+ return off;
+}
+
+static inline int _setup_loops(unsigned dry_run, u8 buf[],
+ const struct _xfer_spec *pxs)
+{
+ struct pl330_xfer *x = pxs->x;
+ u32 ccr = pxs->ccr;
+ unsigned long c, bursts = BYTE_TO_BURST(x->bytes, ccr);
+ int off = 0;
+
+ while (bursts) {
+ c = bursts;
+ off += _loop(dry_run, &buf[off], &c, pxs);
+ bursts -= c;
+ }
+
+ return off;
+}
+
+static inline int _setup_xfer(unsigned dry_run, u8 buf[],
+ const struct _xfer_spec *pxs)
+{
+ struct pl330_xfer *x = pxs->x;
+ int off = 0;
+
+ /* DMAMOV SAR, x->src_addr */
+ off += _emit_MOV(dry_run, &buf[off], SAR, x->src_addr);
+ /* DMAMOV DAR, x->dst_addr */
+ off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr);
+
+ /* Setup Loop(s) */
+ off += _setup_loops(dry_run, &buf[off], pxs);
+
+ return off;
+}
+
+/*
+ * A req is a sequence of one or more xfer units.
+ * Returns the number of bytes taken to setup the MC for the req.
+ */
+static int _setup_req(unsigned dry_run, struct pl330_thread *thrd,
+ unsigned index, struct _xfer_spec *pxs)
+{
+ struct _pl330_req *req = &thrd->req[index];
+ struct pl330_xfer *x;
+ u8 *buf = req->mc_cpu;
+ int off = 0;
+
+ PL330_DBGMC_START(req->mc_bus);
+
+ /* DMAMOV CCR, ccr */
+ off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr);
+
+ x = pxs->r->x;
+ do {
+ /* Error if xfer length is not aligned at burst size */
+ if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr)))
+ return -EINVAL;
+
+ pxs->x = x;
+ off += _setup_xfer(dry_run, &buf[off], pxs);
+
+ x = x->next;
+ } while (x);
+
+ /* DMASEV peripheral/event */
+ off += _emit_SEV(dry_run, &buf[off], thrd->ev);
+ /* DMAEND */
+ off += _emit_END(dry_run, &buf[off]);
+
+ return off;
+}
+
+static inline u32 _prepare_ccr(const struct pl330_reqcfg *rqc)
+{
+ u32 ccr = 0;
+
+ if (rqc->src_inc)
+ ccr |= CC_SRCINC;
+
+ if (rqc->dst_inc)
+ ccr |= CC_DSTINC;
+
+ /* We set same protection levels for Src and DST for now */
+ if (rqc->privileged)
+ ccr |= CC_SRCPRI | CC_DSTPRI;
+ if (rqc->nonsecure)
+ ccr |= CC_SRCNS | CC_DSTNS;
+ if (rqc->insnaccess)
+ ccr |= CC_SRCIA | CC_DSTIA;
+
+ ccr |= (((rqc->brst_len - 1) & 0xf) << CC_SRCBRSTLEN_SHFT);
+ ccr |= (((rqc->brst_len - 1) & 0xf) << CC_DSTBRSTLEN_SHFT);
+
+ ccr |= (rqc->brst_size << CC_SRCBRSTSIZE_SHFT);
+ ccr |= (rqc->brst_size << CC_DSTBRSTSIZE_SHFT);
+
+ ccr |= (rqc->scctl << CC_SRCCCTRL_SHFT);
+ ccr |= (rqc->dcctl << CC_DSTCCTRL_SHFT);
+
+ ccr |= (rqc->swap << CC_SWAP_SHFT);
+
+ return ccr;
+}
+
+static inline bool _is_valid(u32 ccr)
+{
+ enum pl330_dstcachectrl dcctl;
+ enum pl330_srccachectrl scctl;
+
+ dcctl = (ccr >> CC_DSTCCTRL_SHFT) & CC_DRCCCTRL_MASK;
+ scctl = (ccr >> CC_SRCCCTRL_SHFT) & CC_SRCCCTRL_MASK;
+
+ if (dcctl == DINVALID1 || dcctl == DINVALID2
+ || scctl == SINVALID1 || scctl == SINVALID2)
+ return false;
+ else
+ return true;
+}
+
+/*
+ * Submit a list of xfers after which the client wants notification.
+ * Client is not notified after each xfer unit, just once after all
+ * xfer units are done or some error occurs.
+ */
+static int pl330_submit_req(void *ch_id, struct pl330_req *r)
+{
+ struct pl330_thread *thrd = ch_id;
+ struct pl330_dmac *pl330;
+ struct pl330_info *pi;
+ struct _xfer_spec xs;
+ unsigned long flags;
+ void __iomem *regs;
+ unsigned idx;
+ u32 ccr;
+ int ret = 0;
+
+ /* No Req or Unacquired Channel or DMAC */
+ if (!r || !thrd || thrd->free)
+ return -EINVAL;
+
+ pl330 = thrd->dmac;
+ pi = pl330->pinfo;
+ regs = pi->base;
+
+ if (pl330->state == DYING
+ || pl330->dmac_tbd.reset_chan & (1 << thrd->id)) {
+ dev_info(thrd->dmac->pinfo->dev, "%s:%d\n",
+ __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ /* If request for non-existing peripheral */
+ if (r->rqtype != MEMTOMEM && r->peri >= pi->pcfg.num_peri) {
+ dev_info(thrd->dmac->pinfo->dev,
+ "%s:%d Invalid peripheral(%u)!\n",
+ __func__, __LINE__, r->peri);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&pl330->lock, flags);
+
+ if (_queue_full(thrd)) {
+ ret = -EAGAIN;
+ goto xfer_exit;
+ }
+
+ /* Prefer Secure Channel */
+ if (!_manager_ns(thrd))
+ r->cfg->nonsecure = 0;
+ else
+ r->cfg->nonsecure = 1;
+
+ /* Use last settings, if not provided */
+ if (r->cfg)
+ ccr = _prepare_ccr(r->cfg);
+ else
+ ccr = readl(regs + CC(thrd->id));
+
+ /* If this req doesn't have valid xfer settings */
+ if (!_is_valid(ccr)) {
+ ret = -EINVAL;
+ dev_info(thrd->dmac->pinfo->dev, "%s:%d Invalid CCR(%x)!\n",
+ __func__, __LINE__, ccr);
+ goto xfer_exit;
+ }
+
+ idx = IS_FREE(&thrd->req[0]) ? 0 : 1;
+
+ xs.ccr = ccr;
+ xs.r = r;
+
+ /* First dry run to check if req is acceptable */
+ ret = _setup_req(1, thrd, idx, &xs);
+ if (ret < 0)
+ goto xfer_exit;
+
+ if (ret > pi->mcbufsz / 2) {
+ dev_info(thrd->dmac->pinfo->dev,
+ "%s:%d Trying increasing mcbufsz\n",
+ __func__, __LINE__);
+ ret = -ENOMEM;
+ goto xfer_exit;
+ }
+
+ /* Hook the request */
+ thrd->lstenq = idx;
+ thrd->req[idx].mc_len = _setup_req(0, thrd, idx, &xs);
+ thrd->req[idx].r = r;
+
+ ret = 0;
+
+xfer_exit:
+ spin_unlock_irqrestore(&pl330->lock, flags);
+
+ return ret;
+}
+
+static void pl330_dotask(unsigned long data)
+{
+ struct pl330_dmac *pl330 = (struct pl330_dmac *) data;
+ struct pl330_info *pi = pl330->pinfo;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&pl330->lock, flags);
+
+ /* The DMAC itself gone nuts */
+ if (pl330->dmac_tbd.reset_dmac) {
+ pl330->state = DYING;
+ /* Reset the manager too */
+ pl330->dmac_tbd.reset_mngr = true;
+ /* Clear the reset flag */
+ pl330->dmac_tbd.reset_dmac = false;
+ }
+
+ if (pl330->dmac_tbd.reset_mngr) {
+ _stop(pl330->manager);
+ /* Reset all channels */
+ pl330->dmac_tbd.reset_chan = (1 << pi->pcfg.num_chan) - 1;
+ /* Clear the reset flag */
+ pl330->dmac_tbd.reset_mngr = false;
+ }
+
+ for (i = 0; i < pi->pcfg.num_chan; i++) {
+
+ if (pl330->dmac_tbd.reset_chan & (1 << i)) {
+ struct pl330_thread *thrd = &pl330->channels[i];
+ void __iomem *regs = pi->base;
+ enum pl330_op_err err;
+
+ _stop(thrd);
+
+ if (readl(regs + FSC) & (1 << thrd->id))
+ err = PL330_ERR_FAIL;
+ else
+ err = PL330_ERR_ABORT;
+
+ spin_unlock_irqrestore(&pl330->lock, flags);
+
+ _callback(thrd->req[1 - thrd->lstenq].r, err);
+ _callback(thrd->req[thrd->lstenq].r, err);
+
+ spin_lock_irqsave(&pl330->lock, flags);
+
+ thrd->req[0].r = NULL;
+ thrd->req[1].r = NULL;
+ mark_free(thrd, 0);
+ mark_free(thrd, 1);
+
+ /* Clear the reset flag */
+ pl330->dmac_tbd.reset_chan &= ~(1 << i);
+ }
+ }
+
+ spin_unlock_irqrestore(&pl330->lock, flags);
+
+ return;
+}
+
+/* Returns 1 if state was updated, 0 otherwise */
+static int pl330_update(const struct pl330_info *pi)
+{
+ struct _pl330_req *rqdone;
+ struct pl330_dmac *pl330;
+ unsigned long flags;
+ void __iomem *regs;
+ u32 val;
+ int id, ev, ret = 0;
+
+ if (!pi || !pi->pl330_data)
+ return 0;
+
+ regs = pi->base;
+ pl330 = pi->pl330_data;
+
+ spin_lock_irqsave(&pl330->lock, flags);
+
+ val = readl(regs + FSM) & 0x1;
+ if (val)
+ pl330->dmac_tbd.reset_mngr = true;
+ else
+ pl330->dmac_tbd.reset_mngr = false;
+
+ val = readl(regs + FSC) & ((1 << pi->pcfg.num_chan) - 1);
+ pl330->dmac_tbd.reset_chan |= val;
+ if (val) {
+ int i = 0;
+ while (i < pi->pcfg.num_chan) {
+ if (val & (1 << i)) {
+ dev_info(pi->dev,
+ "Reset Channel-%d\t CS-%x FTC-%x\n",
+ i, readl(regs + CS(i)),
+ readl(regs + FTC(i)));
+ _stop(&pl330->channels[i]);
+ }
+ i++;
+ }
+ }
+
+ /* Check which event happened i.e, thread notified */
+ val = readl(regs + ES);
+ if (pi->pcfg.num_events < 32
+ && val & ~((1 << pi->pcfg.num_events) - 1)) {
+ pl330->dmac_tbd.reset_dmac = true;
+ dev_err(pi->dev, "%s:%d Unexpected!\n", __func__, __LINE__);
+ ret = 1;
+ goto updt_exit;
+ }
+
+ for (ev = 0; ev < pi->pcfg.num_events; ev++) {
+ if (val & (1 << ev)) { /* Event occurred */
+ struct pl330_thread *thrd;
+ u32 inten = readl(regs + INTEN);
+ int active;
+
+ /* Clear the event */
+ if (inten & (1 << ev))
+ writel(1 << ev, regs + INTCLR);
+
+ ret = 1;
+
+ id = pl330->events[ev];
+
+ thrd = &pl330->channels[id];
+
+ active = thrd->req_running;
+ if (active == -1) /* Aborted */
+ continue;
+
+ rqdone = &thrd->req[active];
+ mark_free(thrd, active);
+
+ /* Get going again ASAP */
+ _start(thrd);
+
+ /* For now, just make a list of callbacks to be done */
+ list_add_tail(&rqdone->rqd, &pl330->req_done);
+ }
+ }
+
+ /* Now that we are in no hurry, do the callbacks */
+ while (!list_empty(&pl330->req_done)) {
+ struct pl330_req *r;
+
+ rqdone = container_of(pl330->req_done.next,
+ struct _pl330_req, rqd);
+
+ list_del_init(&rqdone->rqd);
+
+ /* Detach the req */
+ r = rqdone->r;
+ rqdone->r = NULL;
+
+ spin_unlock_irqrestore(&pl330->lock, flags);
+ _callback(r, PL330_ERR_NONE);
+ spin_lock_irqsave(&pl330->lock, flags);
+ }
+
+updt_exit:
+ spin_unlock_irqrestore(&pl330->lock, flags);
+
+ if (pl330->dmac_tbd.reset_dmac
+ || pl330->dmac_tbd.reset_mngr
+ || pl330->dmac_tbd.reset_chan) {
+ ret = 1;
+ tasklet_schedule(&pl330->tasks);
+ }
+
+ return ret;
+}
+
+static int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op)
+{
+ struct pl330_thread *thrd = ch_id;
+ struct pl330_dmac *pl330;
+ unsigned long flags;
+ int ret = 0, active;
+
+ if (!thrd || thrd->free || thrd->dmac->state == DYING)
+ return -EINVAL;
+
+ pl330 = thrd->dmac;
+ active = thrd->req_running;
+
+ spin_lock_irqsave(&pl330->lock, flags);
+
+ switch (op) {
+ case PL330_OP_FLUSH:
+ /* Make sure the channel is stopped */
+ _stop(thrd);
+
+ thrd->req[0].r = NULL;
+ thrd->req[1].r = NULL;
+ mark_free(thrd, 0);
+ mark_free(thrd, 1);
+ break;
+
+ case PL330_OP_ABORT:
+ /* Make sure the channel is stopped */
+ _stop(thrd);
+
+ /* ABORT is only for the active req */
+ if (active == -1)
+ break;
+
+ thrd->req[active].r = NULL;
+ mark_free(thrd, active);
+
+ /* Start the next */
+ case PL330_OP_START:
+ if ((active == -1) && !_start(thrd))
+ ret = -EIO;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&pl330->lock, flags);
+ return ret;
+}
+
+/* Reserve an event */
+static inline int _alloc_event(struct pl330_thread *thrd)
+{
+ struct pl330_dmac *pl330 = thrd->dmac;
+ struct pl330_info *pi = pl330->pinfo;
+ int ev;
+
+ for (ev = 0; ev < pi->pcfg.num_events; ev++)
+ if (pl330->events[ev] == -1) {
+ pl330->events[ev] = thrd->id;
+ return ev;
+ }
+
+ return -1;
+}
+
+static bool _chan_ns(const struct pl330_info *pi, int i)
+{
+ return pi->pcfg.irq_ns & (1 << i);
+}
+
+/* Upon success, returns IdentityToken for the
+ * allocated channel, NULL otherwise.
+ */
+static void *pl330_request_channel(const struct pl330_info *pi)
+{
+ struct pl330_thread *thrd = NULL;
+ struct pl330_dmac *pl330;
+ unsigned long flags;
+ int chans, i;
+
+ if (!pi || !pi->pl330_data)
+ return NULL;
+
+ pl330 = pi->pl330_data;
+
+ if (pl330->state == DYING)
+ return NULL;
+
+ chans = pi->pcfg.num_chan;
+
+ spin_lock_irqsave(&pl330->lock, flags);
+
+ for (i = 0; i < chans; i++) {
+ thrd = &pl330->channels[i];
+ if ((thrd->free) && (!_manager_ns(thrd) ||
+ _chan_ns(pi, i))) {
+ thrd->ev = _alloc_event(thrd);
+ if (thrd->ev >= 0) {
+ thrd->free = false;
+ thrd->lstenq = 1;
+ thrd->req[0].r = NULL;
+ mark_free(thrd, 0);
+ thrd->req[1].r = NULL;
+ mark_free(thrd, 1);
+ break;
+ }
+ }
+ thrd = NULL;
+ }
+
+ spin_unlock_irqrestore(&pl330->lock, flags);
+
+ return thrd;
+}
+
+/* Release an event */
+static inline void _free_event(struct pl330_thread *thrd, int ev)
+{
+ struct pl330_dmac *pl330 = thrd->dmac;
+ struct pl330_info *pi = pl330->pinfo;
+
+ /* If the event is valid and was held by the thread */
+ if (ev >= 0 && ev < pi->pcfg.num_events
+ && pl330->events[ev] == thrd->id)
+ pl330->events[ev] = -1;
+}
+
+static void pl330_release_channel(void *ch_id)
+{
+ struct pl330_thread *thrd = ch_id;
+ struct pl330_dmac *pl330;
+ unsigned long flags;
+
+ if (!thrd || thrd->free)
+ return;
+
+ _stop(thrd);
+
+ _callback(thrd->req[1 - thrd->lstenq].r, PL330_ERR_ABORT);
+ _callback(thrd->req[thrd->lstenq].r, PL330_ERR_ABORT);
+
+ pl330 = thrd->dmac;
+
+ spin_lock_irqsave(&pl330->lock, flags);
+ _free_event(thrd, thrd->ev);
+ thrd->free = true;
+ spin_unlock_irqrestore(&pl330->lock, flags);
+}
+
+/* Initialize the structure for PL330 configuration, that can be used
+ * by the client driver the make best use of the DMAC
+ */
+static void read_dmac_config(struct pl330_info *pi)
+{
+ void __iomem *regs = pi->base;
+ u32 val;
+
+ val = readl(regs + CRD) >> CRD_DATA_WIDTH_SHIFT;
+ val &= CRD_DATA_WIDTH_MASK;
+ pi->pcfg.data_bus_width = 8 * (1 << val);
+
+ val = readl(regs + CRD) >> CRD_DATA_BUFF_SHIFT;
+ val &= CRD_DATA_BUFF_MASK;
+ pi->pcfg.data_buf_dep = val + 1;
+
+ val = readl(regs + CR0) >> CR0_NUM_CHANS_SHIFT;
+ val &= CR0_NUM_CHANS_MASK;
+ val += 1;
+ pi->pcfg.num_chan = val;
+
+ val = readl(regs + CR0);
+ if (val & CR0_PERIPH_REQ_SET) {
+ val = (val >> CR0_NUM_PERIPH_SHIFT) & CR0_NUM_PERIPH_MASK;
+ val += 1;
+ pi->pcfg.num_peri = val;
+ pi->pcfg.peri_ns = readl(regs + CR4);
+ } else {
+ pi->pcfg.num_peri = 0;
+ }
+
+ val = readl(regs + CR0);
+ if (val & CR0_BOOT_MAN_NS)
+ pi->pcfg.mode |= DMAC_MODE_NS;
+ else
+ pi->pcfg.mode &= ~DMAC_MODE_NS;
+
+ val = readl(regs + CR0) >> CR0_NUM_EVENTS_SHIFT;
+ val &= CR0_NUM_EVENTS_MASK;
+ val += 1;
+ pi->pcfg.num_events = val;
+
+ pi->pcfg.irq_ns = readl(regs + CR3);
+
+ pi->pcfg.periph_id = get_id(pi, PERIPH_ID);
+ pi->pcfg.pcell_id = get_id(pi, PCELL_ID);
+}
+
+static inline void _reset_thread(struct pl330_thread *thrd)
+{
+ struct pl330_dmac *pl330 = thrd->dmac;
+ struct pl330_info *pi = pl330->pinfo;
+
+ thrd->req[0].mc_cpu = pl330->mcode_cpu
+ + (thrd->id * pi->mcbufsz);
+ thrd->req[0].mc_bus = pl330->mcode_bus
+ + (thrd->id * pi->mcbufsz);
+ thrd->req[0].r = NULL;
+ mark_free(thrd, 0);
+
+ thrd->req[1].mc_cpu = thrd->req[0].mc_cpu
+ + pi->mcbufsz / 2;
+ thrd->req[1].mc_bus = thrd->req[0].mc_bus
+ + pi->mcbufsz / 2;
+ thrd->req[1].r = NULL;
+ mark_free(thrd, 1);
+}
+
+static int dmac_alloc_threads(struct pl330_dmac *pl330)
+{
+ struct pl330_info *pi = pl330->pinfo;
+ int chans = pi->pcfg.num_chan;
+ struct pl330_thread *thrd;
+ int i;
+
+ /* Allocate 1 Manager and 'chans' Channel threads */
+ pl330->channels = kzalloc((1 + chans) * sizeof(*thrd),
+ GFP_KERNEL);
+ if (!pl330->channels)
+ return -ENOMEM;
+
+ /* Init Channel threads */
+ for (i = 0; i < chans; i++) {
+ thrd = &pl330->channels[i];
+ thrd->id = i;
+ thrd->dmac = pl330;
+ _reset_thread(thrd);
+ thrd->free = true;
+ }
+
+ /* MANAGER is indexed at the end */
+ thrd = &pl330->channels[chans];
+ thrd->id = chans;
+ thrd->dmac = pl330;
+ thrd->free = false;
+ pl330->manager = thrd;
+
+ return 0;
+}
+
+static int dmac_alloc_resources(struct pl330_dmac *pl330)
+{
+ struct pl330_info *pi = pl330->pinfo;
+ int chans = pi->pcfg.num_chan;
+ int ret;
+
+ /*
+ * Alloc MicroCode buffer for 'chans' Channel threads.
+ * A channel's buffer offset is (Channel_Id * MCODE_BUFF_PERCHAN)
+ */
+ pl330->mcode_cpu = dma_alloc_coherent(pi->dev,
+ chans * pi->mcbufsz,
+ &pl330->mcode_bus, GFP_KERNEL);
+ if (!pl330->mcode_cpu) {
+ dev_err(pi->dev, "%s:%d Can't allocate memory!\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ ret = dmac_alloc_threads(pl330);
+ if (ret) {
+ dev_err(pi->dev, "%s:%d Can't to create channels for DMAC!\n",
+ __func__, __LINE__);
+ dma_free_coherent(pi->dev,
+ chans * pi->mcbufsz,
+ pl330->mcode_cpu, pl330->mcode_bus);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pl330_add(struct pl330_info *pi)
+{
+ struct pl330_dmac *pl330;
+ void __iomem *regs;
+ int i, ret;
+
+ if (!pi || !pi->dev)
+ return -EINVAL;
+
+ /* If already added */
+ if (pi->pl330_data)
+ return -EINVAL;
+
+ /*
+ * If the SoC can perform reset on the DMAC, then do it
+ * before reading its configuration.
+ */
+ if (pi->dmac_reset)
+ pi->dmac_reset(pi);
+
+ regs = pi->base;
+
+ /* Check if we can handle this DMAC */
+ if ((get_id(pi, PERIPH_ID) & 0xfffff) != PERIPH_ID_VAL
+ || get_id(pi, PCELL_ID) != PCELL_ID_VAL) {
+ dev_err(pi->dev, "PERIPH_ID 0x%x, PCELL_ID 0x%x !\n",
+ get_id(pi, PERIPH_ID), get_id(pi, PCELL_ID));
+ return -EINVAL;
+ }
+
+ /* Read the configuration of the DMAC */
+ read_dmac_config(pi);
+
+ if (pi->pcfg.num_events == 0) {
+ dev_err(pi->dev, "%s:%d Can't work without events!\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ pl330 = kzalloc(sizeof(*pl330), GFP_KERNEL);
+ if (!pl330) {
+ dev_err(pi->dev, "%s:%d Can't allocate memory!\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ /* Assign the info structure and private data */
+ pl330->pinfo = pi;
+ pi->pl330_data = pl330;
+
+ spin_lock_init(&pl330->lock);
+
+ INIT_LIST_HEAD(&pl330->req_done);
+
+ /* Use default MC buffer size if not provided */
+ if (!pi->mcbufsz)
+ pi->mcbufsz = MCODE_BUFF_PER_REQ * 2;
+
+ /* Mark all events as free */
+ for (i = 0; i < pi->pcfg.num_events; i++)
+ pl330->events[i] = -1;
+
+ /* Allocate resources needed by the DMAC */
+ ret = dmac_alloc_resources(pl330);
+ if (ret) {
+ dev_err(pi->dev, "Unable to create channels for DMAC\n");
+ kfree(pl330);
+ return ret;
+ }
+
+ tasklet_init(&pl330->tasks, pl330_dotask, (unsigned long) pl330);
+
+ pl330->state = INIT;
+
+ return 0;
+}
+
+static int dmac_free_threads(struct pl330_dmac *pl330)
+{
+ struct pl330_info *pi = pl330->pinfo;
+ int chans = pi->pcfg.num_chan;
+ struct pl330_thread *thrd;
+ int i;
+
+ /* Release Channel threads */
+ for (i = 0; i < chans; i++) {
+ thrd = &pl330->channels[i];
+ pl330_release_channel((void *)thrd);
+ }
+
+ /* Free memory */
+ kfree(pl330->channels);
+
+ return 0;
+}
+
+static void dmac_free_resources(struct pl330_dmac *pl330)
+{
+ struct pl330_info *pi = pl330->pinfo;
+ int chans = pi->pcfg.num_chan;
+
+ dmac_free_threads(pl330);
+
+ dma_free_coherent(pi->dev, chans * pi->mcbufsz,
+ pl330->mcode_cpu, pl330->mcode_bus);
+}
+
+static void pl330_del(struct pl330_info *pi)
+{
+ struct pl330_dmac *pl330;
+
+ if (!pi || !pi->pl330_data)
+ return;
+
+ pl330 = pi->pl330_data;
+
+ pl330->state = UNINIT;
+
+ tasklet_kill(&pl330->tasks);
+
+ /* Free DMAC resources */
+ dmac_free_resources(pl330);
+
+ kfree(pl330);
+ pi->pl330_data = NULL;
+}
+
/* forward declaration */
static struct amba_driver pl330_driver;
@@ -234,7 +2320,7 @@ static void pl330_tasklet(unsigned long data)
/* Pick up ripe tomatoes */
list_for_each_entry_safe(desc, _dt, &pch->work_list, node)
if (desc->status == DONE) {
- pch->completed = desc->txd.cookie;
+ dma_cookie_complete(&desc->txd);
list_move_tail(&desc->node, &list);
}
@@ -305,7 +2391,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
spin_lock_irqsave(&pch->lock, flags);
- pch->completed = chan->cookie = 1;
+ dma_cookie_init(chan);
pch->cyclic = false;
pch->pl330_chid = pl330_request_channel(&pdmac->pif);
@@ -340,7 +2426,6 @@ static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned
/* Mark all desc done */
list_for_each_entry_safe(desc, _dt, &pch->work_list , node) {
desc->status = DONE;
- pch->completed = desc->txd.cookie;
list_move_tail(&desc->node, &list);
}
@@ -396,18 +2481,7 @@ static enum dma_status
pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
- struct dma_pl330_chan *pch = to_pchan(chan);
- dma_cookie_t last_done, last_used;
- int ret;
-
- last_done = pch->completed;
- last_used = chan->cookie;
-
- ret = dma_async_is_complete(cookie, last_done, last_used);
-
- dma_set_tx_state(txstate, last_done, last_used, 0);
-
- return ret;
+ return dma_cookie_status(chan, cookie, txstate);
}
static void pl330_issue_pending(struct dma_chan *chan)
@@ -430,26 +2504,16 @@ static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx)
spin_lock_irqsave(&pch->lock, flags);
/* Assign cookies to all nodes */
- cookie = tx->chan->cookie;
-
while (!list_empty(&last->node)) {
desc = list_entry(last->node.next, struct dma_pl330_desc, node);
- if (++cookie < 0)
- cookie = 1;
- desc->txd.cookie = cookie;
+ dma_cookie_assign(&desc->txd);
list_move_tail(&desc->node, &pch->work_list);
}
- if (++cookie < 0)
- cookie = 1;
- last->txd.cookie = cookie;
-
+ cookie = dma_cookie_assign(&last->txd);
list_add_tail(&last->node, &pch->work_list);
-
- tx->chan->cookie = cookie;
-
spin_unlock_irqrestore(&pch->lock, flags);
return cookie;
@@ -553,6 +2617,7 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch)
async_tx_ack(&desc->txd);
desc->req.peri = peri_id ? pch->chan.chan_id : 0;
+ desc->rqcfg.pcfg = &pch->dmac->pif.pcfg;
dma_async_tx_descriptor_init(&desc->txd, &pch->chan);
@@ -621,7 +2686,8 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t dma_addr, size_t len,
- size_t period_len, enum dma_transfer_direction direction)
+ size_t period_len, enum dma_transfer_direction direction,
+ void *context)
{
struct dma_pl330_desc *desc;
struct dma_pl330_chan *pch = to_pchan(chan);
@@ -711,7 +2777,7 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
static struct dma_async_tx_descriptor *
pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
- unsigned long flg)
+ unsigned long flg, void *context)
{
struct dma_pl330_desc *first, *desc = NULL;
struct dma_pl330_chan *pch = to_pchan(chan);
@@ -829,7 +2895,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
if (IS_ERR(pdmac->clk)) {
dev_err(&adev->dev, "Cannot get operation clock.\n");
ret = -EINVAL;
- goto probe_err1;
+ goto probe_err2;
}
amba_set_drvdata(adev, pdmac);
@@ -843,11 +2909,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
ret = request_irq(irq, pl330_irq_handler, 0,
dev_name(&adev->dev), pi);
if (ret)
- goto probe_err2;
+ goto probe_err3;
ret = pl330_add(pi);
if (ret)
- goto probe_err3;
+ goto probe_err4;
INIT_LIST_HEAD(&pdmac->desc_pool);
spin_lock_init(&pdmac->pool_lock);
@@ -904,7 +2970,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
ret = dma_async_device_register(pd);
if (ret) {
dev_err(&adev->dev, "unable to register DMAC\n");
- goto probe_err4;
+ goto probe_err5;
}
dev_info(&adev->dev,
@@ -917,10 +2983,15 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
return 0;
-probe_err4:
+probe_err5:
pl330_del(pi);
-probe_err3:
+probe_err4:
free_irq(irq, pi);
+probe_err3:
+#ifndef CONFIG_PM_RUNTIME
+ clk_disable(pdmac->clk);
+#endif
+ clk_put(pdmac->clk);
probe_err2:
iounmap(pi->base);
probe_err1:
diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c
index fc457a7..ced9882 100644
--- a/drivers/dma/ppc4xx/adma.c
+++ b/drivers/dma/ppc4xx/adma.c
@@ -46,6 +46,7 @@
#include <asm/dcr.h>
#include <asm/dcr-regs.h>
#include "adma.h"
+#include "../dmaengine.h"
enum ppc_adma_init_code {
PPC_ADMA_INIT_OK = 0,
@@ -1930,7 +1931,7 @@ static void __ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan)
if (end_of_chain && slot_cnt) {
/* Should wait for ZeroSum completion */
if (cookie > 0)
- chan->completed_cookie = cookie;
+ chan->common.completed_cookie = cookie;
return;
}
@@ -1960,7 +1961,7 @@ static void __ppc440spe_adma_slot_cleanup(struct ppc440spe_adma_chan *chan)
BUG_ON(!seen_current);
if (cookie > 0) {
- chan->completed_cookie = cookie;
+ chan->common.completed_cookie = cookie;
pr_debug("\tcompleted cookie %d\n", cookie);
}
@@ -2150,22 +2151,6 @@ static int ppc440spe_adma_alloc_chan_resources(struct dma_chan *chan)
}
/**
- * ppc440spe_desc_assign_cookie - assign a cookie
- */
-static dma_cookie_t ppc440spe_desc_assign_cookie(
- struct ppc440spe_adma_chan *chan,
- struct ppc440spe_adma_desc_slot *desc)
-{
- dma_cookie_t cookie = chan->common.cookie;
-
- cookie++;
- if (cookie < 0)
- cookie = 1;
- chan->common.cookie = desc->async_tx.cookie = cookie;
- return cookie;
-}
-
-/**
* ppc440spe_rxor_set_region_data -
*/
static void ppc440spe_rxor_set_region(struct ppc440spe_adma_desc_slot *desc,
@@ -2235,8 +2220,7 @@ static dma_cookie_t ppc440spe_adma_tx_submit(struct dma_async_tx_descriptor *tx)
slots_per_op = group_start->slots_per_op;
spin_lock_bh(&chan->lock);
-
- cookie = ppc440spe_desc_assign_cookie(chan, sw_desc);
+ cookie = dma_cookie_assign(tx);
if (unlikely(list_empty(&chan->chain))) {
/* first peer */
@@ -3944,28 +3928,16 @@ static enum dma_status ppc440spe_adma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *txstate)
{
struct ppc440spe_adma_chan *ppc440spe_chan;
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
enum dma_status ret;
ppc440spe_chan = to_ppc440spe_adma_chan(chan);
- last_used = chan->cookie;
- last_complete = ppc440spe_chan->completed_cookie;
-
- dma_set_tx_state(txstate, last_complete, last_used, 0);
-
- ret = dma_async_is_complete(cookie, last_complete, last_used);
+ ret = dma_cookie_status(chan, cookie, txstate);
if (ret == DMA_SUCCESS)
return ret;
ppc440spe_adma_slot_cleanup(ppc440spe_chan);
- last_used = chan->cookie;
- last_complete = ppc440spe_chan->completed_cookie;
-
- dma_set_tx_state(txstate, last_complete, last_used, 0);
-
- return dma_async_is_complete(cookie, last_complete, last_used);
+ return dma_cookie_status(chan, cookie, txstate);
}
/**
@@ -4050,16 +4022,12 @@ static void ppc440spe_chan_start_null_xor(struct ppc440spe_adma_chan *chan)
async_tx_ack(&sw_desc->async_tx);
ppc440spe_desc_init_null_xor(group_start);
- cookie = chan->common.cookie;
- cookie++;
- if (cookie <= 1)
- cookie = 2;
+ cookie = dma_cookie_assign(&sw_desc->async_tx);
/* initialize the completed cookie to be less than
* the most recently used cookie
*/
- chan->completed_cookie = cookie - 1;
- chan->common.cookie = sw_desc->async_tx.cookie = cookie;
+ chan->common.completed_cookie = cookie - 1;
/* channel should not be busy */
BUG_ON(ppc440spe_chan_is_busy(chan));
@@ -4529,6 +4497,7 @@ static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev)
INIT_LIST_HEAD(&chan->all_slots);
chan->device = adev;
chan->common.device = &adev->common;
+ dma_cookie_init(&chan->common);
list_add_tail(&chan->common.device_node, &adev->common.channels);
tasklet_init(&chan->irq_tasklet, ppc440spe_adma_tasklet,
(unsigned long)chan);
diff --git a/drivers/dma/ppc4xx/adma.h b/drivers/dma/ppc4xx/adma.h
index 8ada5a81..26b7a5e 100644
--- a/drivers/dma/ppc4xx/adma.h
+++ b/drivers/dma/ppc4xx/adma.h
@@ -81,7 +81,6 @@ struct ppc440spe_adma_device {
* @common: common dmaengine channel object members
* @all_slots: complete domain of slots usable by the channel
* @pending: allows batching of hardware operations
- * @completed_cookie: identifier for the most recently completed operation
* @slots_allocated: records the actual size of the descriptor slot pool
* @hw_chain_inited: h/w descriptor chain initialization flag
* @irq_tasklet: bottom half where ppc440spe_adma_slot_cleanup runs
@@ -99,7 +98,6 @@ struct ppc440spe_adma_chan {
struct list_head all_slots;
struct ppc440spe_adma_desc_slot *last_used;
int pending;
- dma_cookie_t completed_cookie;
int slots_allocated;
int hw_chain_inited;
struct tasklet_struct irq_tasklet;
diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c
new file mode 100644
index 0000000..16a6b48
--- /dev/null
+++ b/drivers/dma/sa11x0-dma.c
@@ -0,0 +1,1109 @@
+/*
+ * SA11x0 DMAengine support
+ *
+ * Copyright (C) 2012 Russell King
+ * Derived in part from arch/arm/mach-sa1100/dma.c,
+ * Copyright (C) 2000, 2001 by Nicolas Pitre
+ *
+ * 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/sched.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sa11x0-dma.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define NR_PHY_CHAN 6
+#define DMA_ALIGN 3
+#define DMA_MAX_SIZE 0x1fff
+#define DMA_CHUNK_SIZE 0x1000
+
+#define DMA_DDAR 0x00
+#define DMA_DCSR_S 0x04
+#define DMA_DCSR_C 0x08
+#define DMA_DCSR_R 0x0c
+#define DMA_DBSA 0x10
+#define DMA_DBTA 0x14
+#define DMA_DBSB 0x18
+#define DMA_DBTB 0x1c
+#define DMA_SIZE 0x20
+
+#define DCSR_RUN (1 << 0)
+#define DCSR_IE (1 << 1)
+#define DCSR_ERROR (1 << 2)
+#define DCSR_DONEA (1 << 3)
+#define DCSR_STRTA (1 << 4)
+#define DCSR_DONEB (1 << 5)
+#define DCSR_STRTB (1 << 6)
+#define DCSR_BIU (1 << 7)
+
+#define DDAR_RW (1 << 0) /* 0 = W, 1 = R */
+#define DDAR_E (1 << 1) /* 0 = LE, 1 = BE */
+#define DDAR_BS (1 << 2) /* 0 = BS4, 1 = BS8 */
+#define DDAR_DW (1 << 3) /* 0 = 8b, 1 = 16b */
+#define DDAR_Ser0UDCTr (0x0 << 4)
+#define DDAR_Ser0UDCRc (0x1 << 4)
+#define DDAR_Ser1SDLCTr (0x2 << 4)
+#define DDAR_Ser1SDLCRc (0x3 << 4)
+#define DDAR_Ser1UARTTr (0x4 << 4)
+#define DDAR_Ser1UARTRc (0x5 << 4)
+#define DDAR_Ser2ICPTr (0x6 << 4)
+#define DDAR_Ser2ICPRc (0x7 << 4)
+#define DDAR_Ser3UARTTr (0x8 << 4)
+#define DDAR_Ser3UARTRc (0x9 << 4)
+#define DDAR_Ser4MCP0Tr (0xa << 4)
+#define DDAR_Ser4MCP0Rc (0xb << 4)
+#define DDAR_Ser4MCP1Tr (0xc << 4)
+#define DDAR_Ser4MCP1Rc (0xd << 4)
+#define DDAR_Ser4SSPTr (0xe << 4)
+#define DDAR_Ser4SSPRc (0xf << 4)
+
+struct sa11x0_dma_sg {
+ u32 addr;
+ u32 len;
+};
+
+struct sa11x0_dma_desc {
+ struct dma_async_tx_descriptor tx;
+ u32 ddar;
+ size_t size;
+
+ /* maybe protected by c->lock */
+ struct list_head node;
+ unsigned sglen;
+ struct sa11x0_dma_sg sg[0];
+};
+
+struct sa11x0_dma_phy;
+
+struct sa11x0_dma_chan {
+ struct dma_chan chan;
+ spinlock_t lock;
+ dma_cookie_t lc;
+
+ /* protected by c->lock */
+ struct sa11x0_dma_phy *phy;
+ enum dma_status status;
+ struct list_head desc_submitted;
+ struct list_head desc_issued;
+
+ /* protected by d->lock */
+ struct list_head node;
+
+ u32 ddar;
+ const char *name;
+};
+
+struct sa11x0_dma_phy {
+ void __iomem *base;
+ struct sa11x0_dma_dev *dev;
+ unsigned num;
+
+ struct sa11x0_dma_chan *vchan;
+
+ /* Protected by c->lock */
+ unsigned sg_load;
+ struct sa11x0_dma_desc *txd_load;
+ unsigned sg_done;
+ struct sa11x0_dma_desc *txd_done;
+#ifdef CONFIG_PM_SLEEP
+ u32 dbs[2];
+ u32 dbt[2];
+ u32 dcsr;
+#endif
+};
+
+struct sa11x0_dma_dev {
+ struct dma_device slave;
+ void __iomem *base;
+ spinlock_t lock;
+ struct tasklet_struct task;
+ struct list_head chan_pending;
+ struct list_head desc_complete;
+ struct sa11x0_dma_phy phy[NR_PHY_CHAN];
+};
+
+static struct sa11x0_dma_chan *to_sa11x0_dma_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct sa11x0_dma_chan, chan);
+}
+
+static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev)
+{
+ return container_of(dmadev, struct sa11x0_dma_dev, slave);
+}
+
+static struct sa11x0_dma_desc *to_sa11x0_dma_tx(struct dma_async_tx_descriptor *tx)
+{
+ return container_of(tx, struct sa11x0_dma_desc, tx);
+}
+
+static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c)
+{
+ if (list_empty(&c->desc_issued))
+ return NULL;
+
+ return list_first_entry(&c->desc_issued, struct sa11x0_dma_desc, node);
+}
+
+static void sa11x0_dma_start_desc(struct sa11x0_dma_phy *p, struct sa11x0_dma_desc *txd)
+{
+ list_del(&txd->node);
+ p->txd_load = txd;
+ p->sg_load = 0;
+
+ dev_vdbg(p->dev->slave.dev, "pchan %u: txd %p[%x]: starting: DDAR:%x\n",
+ p->num, txd, txd->tx.cookie, txd->ddar);
+}
+
+static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p,
+ struct sa11x0_dma_chan *c)
+{
+ struct sa11x0_dma_desc *txd = p->txd_load;
+ struct sa11x0_dma_sg *sg;
+ void __iomem *base = p->base;
+ unsigned dbsx, dbtx;
+ u32 dcsr;
+
+ if (!txd)
+ return;
+
+ dcsr = readl_relaxed(base + DMA_DCSR_R);
+
+ /* Don't try to load the next transfer if both buffers are started */
+ if ((dcsr & (DCSR_STRTA | DCSR_STRTB)) == (DCSR_STRTA | DCSR_STRTB))
+ return;
+
+ if (p->sg_load == txd->sglen) {
+ struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c);
+
+ /*
+ * We have reached the end of the current descriptor.
+ * Peek at the next descriptor, and if compatible with
+ * the current, start processing it.
+ */
+ if (txn && txn->ddar == txd->ddar) {
+ txd = txn;
+ sa11x0_dma_start_desc(p, txn);
+ } else {
+ p->txd_load = NULL;
+ return;
+ }
+ }
+
+ sg = &txd->sg[p->sg_load++];
+
+ /* Select buffer to load according to channel status */
+ if (((dcsr & (DCSR_BIU | DCSR_STRTB)) == (DCSR_BIU | DCSR_STRTB)) ||
+ ((dcsr & (DCSR_BIU | DCSR_STRTA)) == 0)) {
+ dbsx = DMA_DBSA;
+ dbtx = DMA_DBTA;
+ dcsr = DCSR_STRTA | DCSR_IE | DCSR_RUN;
+ } else {
+ dbsx = DMA_DBSB;
+ dbtx = DMA_DBTB;
+ dcsr = DCSR_STRTB | DCSR_IE | DCSR_RUN;
+ }
+
+ writel_relaxed(sg->addr, base + dbsx);
+ writel_relaxed(sg->len, base + dbtx);
+ writel(dcsr, base + DMA_DCSR_S);
+
+ dev_dbg(p->dev->slave.dev, "pchan %u: load: DCSR:%02x DBS%c:%08x DBT%c:%08x\n",
+ p->num, dcsr,
+ 'A' + (dbsx == DMA_DBSB), sg->addr,
+ 'A' + (dbtx == DMA_DBTB), sg->len);
+}
+
+static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p,
+ struct sa11x0_dma_chan *c)
+{
+ struct sa11x0_dma_desc *txd = p->txd_done;
+
+ if (++p->sg_done == txd->sglen) {
+ struct sa11x0_dma_dev *d = p->dev;
+
+ dev_vdbg(d->slave.dev, "pchan %u: txd %p[%x]: completed\n",
+ p->num, p->txd_done, p->txd_done->tx.cookie);
+
+ c->lc = txd->tx.cookie;
+
+ spin_lock(&d->lock);
+ list_add_tail(&txd->node, &d->desc_complete);
+ spin_unlock(&d->lock);
+
+ p->sg_done = 0;
+ p->txd_done = p->txd_load;
+
+ tasklet_schedule(&d->task);
+ }
+
+ sa11x0_dma_start_sg(p, c);
+}
+
+static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id)
+{
+ struct sa11x0_dma_phy *p = dev_id;
+ struct sa11x0_dma_dev *d = p->dev;
+ struct sa11x0_dma_chan *c;
+ u32 dcsr;
+
+ dcsr = readl_relaxed(p->base + DMA_DCSR_R);
+ if (!(dcsr & (DCSR_ERROR | DCSR_DONEA | DCSR_DONEB)))
+ return IRQ_NONE;
+
+ /* Clear reported status bits */
+ writel_relaxed(dcsr & (DCSR_ERROR | DCSR_DONEA | DCSR_DONEB),
+ p->base + DMA_DCSR_C);
+
+ dev_dbg(d->slave.dev, "pchan %u: irq: DCSR:%02x\n", p->num, dcsr);
+
+ if (dcsr & DCSR_ERROR) {
+ dev_err(d->slave.dev, "pchan %u: error. DCSR:%02x DDAR:%08x DBSA:%08x DBTA:%08x DBSB:%08x DBTB:%08x\n",
+ p->num, dcsr,
+ readl_relaxed(p->base + DMA_DDAR),
+ readl_relaxed(p->base + DMA_DBSA),
+ readl_relaxed(p->base + DMA_DBTA),
+ readl_relaxed(p->base + DMA_DBSB),
+ readl_relaxed(p->base + DMA_DBTB));
+ }
+
+ c = p->vchan;
+ if (c) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->lock, flags);
+ /*
+ * Now that we're holding the lock, check that the vchan
+ * really is associated with this pchan before touching the
+ * hardware. This should always succeed, because we won't
+ * change p->vchan or c->phy while the channel is actively
+ * transferring.
+ */
+ if (c->phy == p) {
+ if (dcsr & DCSR_DONEA)
+ sa11x0_dma_complete(p, c);
+ if (dcsr & DCSR_DONEB)
+ sa11x0_dma_complete(p, c);
+ }
+ spin_unlock_irqrestore(&c->lock, flags);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void sa11x0_dma_start_txd(struct sa11x0_dma_chan *c)
+{
+ struct sa11x0_dma_desc *txd = sa11x0_dma_next_desc(c);
+
+ /* If the issued list is empty, we have no further txds to process */
+ if (txd) {
+ struct sa11x0_dma_phy *p = c->phy;
+
+ sa11x0_dma_start_desc(p, txd);
+ p->txd_done = txd;
+ p->sg_done = 0;
+
+ /* The channel should not have any transfers started */
+ WARN_ON(readl_relaxed(p->base + DMA_DCSR_R) &
+ (DCSR_STRTA | DCSR_STRTB));
+
+ /* Clear the run and start bits before changing DDAR */
+ writel_relaxed(DCSR_RUN | DCSR_STRTA | DCSR_STRTB,
+ p->base + DMA_DCSR_C);
+ writel_relaxed(txd->ddar, p->base + DMA_DDAR);
+
+ /* Try to start both buffers */
+ sa11x0_dma_start_sg(p, c);
+ sa11x0_dma_start_sg(p, c);
+ }
+}
+
+static void sa11x0_dma_tasklet(unsigned long arg)
+{
+ struct sa11x0_dma_dev *d = (struct sa11x0_dma_dev *)arg;
+ struct sa11x0_dma_phy *p;
+ struct sa11x0_dma_chan *c;
+ struct sa11x0_dma_desc *txd, *txn;
+ LIST_HEAD(head);
+ unsigned pch, pch_alloc = 0;
+
+ dev_dbg(d->slave.dev, "tasklet enter\n");
+
+ /* Get the completed tx descriptors */
+ spin_lock_irq(&d->lock);
+ list_splice_init(&d->desc_complete, &head);
+ spin_unlock_irq(&d->lock);
+
+ list_for_each_entry(txd, &head, node) {
+ c = to_sa11x0_dma_chan(txd->tx.chan);
+
+ dev_dbg(d->slave.dev, "vchan %p: txd %p[%x] completed\n",
+ c, txd, txd->tx.cookie);
+
+ spin_lock_irq(&c->lock);
+ p = c->phy;
+ if (p) {
+ if (!p->txd_done)
+ sa11x0_dma_start_txd(c);
+ if (!p->txd_done) {
+ /* No current txd associated with this channel */
+ dev_dbg(d->slave.dev, "pchan %u: free\n", p->num);
+
+ /* Mark this channel free */
+ c->phy = NULL;
+ p->vchan = NULL;
+ }
+ }
+ spin_unlock_irq(&c->lock);
+ }
+
+ spin_lock_irq(&d->lock);
+ for (pch = 0; pch < NR_PHY_CHAN; pch++) {
+ p = &d->phy[pch];
+
+ if (p->vchan == NULL && !list_empty(&d->chan_pending)) {
+ c = list_first_entry(&d->chan_pending,
+ struct sa11x0_dma_chan, node);
+ list_del_init(&c->node);
+
+ pch_alloc |= 1 << pch;
+
+ /* Mark this channel allocated */
+ p->vchan = c;
+
+ dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, c);
+ }
+ }
+ spin_unlock_irq(&d->lock);
+
+ for (pch = 0; pch < NR_PHY_CHAN; pch++) {
+ if (pch_alloc & (1 << pch)) {
+ p = &d->phy[pch];
+ c = p->vchan;
+
+ spin_lock_irq(&c->lock);
+ c->phy = p;
+
+ sa11x0_dma_start_txd(c);
+ spin_unlock_irq(&c->lock);
+ }
+ }
+
+ /* Now free the completed tx descriptor, and call their callbacks */
+ list_for_each_entry_safe(txd, txn, &head, node) {
+ dma_async_tx_callback callback = txd->tx.callback;
+ void *callback_param = txd->tx.callback_param;
+
+ dev_dbg(d->slave.dev, "txd %p[%x]: callback and free\n",
+ txd, txd->tx.cookie);
+
+ kfree(txd);
+
+ if (callback)
+ callback(callback_param);
+ }
+
+ dev_dbg(d->slave.dev, "tasklet exit\n");
+}
+
+
+static void sa11x0_dma_desc_free(struct sa11x0_dma_dev *d, struct list_head *head)
+{
+ struct sa11x0_dma_desc *txd, *txn;
+
+ list_for_each_entry_safe(txd, txn, head, node) {
+ dev_dbg(d->slave.dev, "txd %p: freeing\n", txd);
+ kfree(txd);
+ }
+}
+
+static int sa11x0_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+ return 0;
+}
+
+static void sa11x0_dma_free_chan_resources(struct dma_chan *chan)
+{
+ struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
+ struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&c->lock, flags);
+ spin_lock(&d->lock);
+ list_del_init(&c->node);
+ spin_unlock(&d->lock);
+
+ list_splice_tail_init(&c->desc_submitted, &head);
+ list_splice_tail_init(&c->desc_issued, &head);
+ spin_unlock_irqrestore(&c->lock, flags);
+
+ sa11x0_dma_desc_free(d, &head);
+}
+
+static dma_addr_t sa11x0_dma_pos(struct sa11x0_dma_phy *p)
+{
+ unsigned reg;
+ u32 dcsr;
+
+ dcsr = readl_relaxed(p->base + DMA_DCSR_R);
+
+ if ((dcsr & (DCSR_BIU | DCSR_STRTA)) == DCSR_STRTA ||
+ (dcsr & (DCSR_BIU | DCSR_STRTB)) == DCSR_BIU)
+ reg = DMA_DBSA;
+ else
+ reg = DMA_DBSB;
+
+ return readl_relaxed(p->base + reg);
+}
+
+static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie, struct dma_tx_state *state)
+{
+ struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
+ struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
+ struct sa11x0_dma_phy *p;
+ struct sa11x0_dma_desc *txd;
+ dma_cookie_t last_used, last_complete;
+ unsigned long flags;
+ enum dma_status ret;
+ size_t bytes = 0;
+
+ last_used = c->chan.cookie;
+ last_complete = c->lc;
+
+ ret = dma_async_is_complete(cookie, last_complete, last_used);
+ if (ret == DMA_SUCCESS) {
+ dma_set_tx_state(state, last_complete, last_used, 0);
+ return ret;
+ }
+
+ spin_lock_irqsave(&c->lock, flags);
+ p = c->phy;
+ ret = c->status;
+ if (p) {
+ dma_addr_t addr = sa11x0_dma_pos(p);
+
+ dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr);
+
+ txd = p->txd_done;
+ if (txd) {
+ unsigned i;
+
+ for (i = 0; i < txd->sglen; i++) {
+ dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x\n",
+ i, txd->sg[i].addr, txd->sg[i].len);
+ if (addr >= txd->sg[i].addr &&
+ addr < txd->sg[i].addr + txd->sg[i].len) {
+ unsigned len;
+
+ len = txd->sg[i].len -
+ (addr - txd->sg[i].addr);
+ dev_vdbg(d->slave.dev, "tx_status: [%u] +%x\n",
+ i, len);
+ bytes += len;
+ i++;
+ break;
+ }
+ }
+ for (; i < txd->sglen; i++) {
+ dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x ++\n",
+ i, txd->sg[i].addr, txd->sg[i].len);
+ bytes += txd->sg[i].len;
+ }
+ }
+ if (txd != p->txd_load && p->txd_load)
+ bytes += p->txd_load->size;
+ }
+ list_for_each_entry(txd, &c->desc_issued, node) {
+ bytes += txd->size;
+ }
+ spin_unlock_irqrestore(&c->lock, flags);
+
+ dma_set_tx_state(state, last_complete, last_used, bytes);
+
+ dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", bytes);
+
+ return ret;
+}
+
+/*
+ * Move pending txds to the issued list, and re-init pending list.
+ * If not already pending, add this channel to the list of pending
+ * channels and trigger the tasklet to run.
+ */
+static void sa11x0_dma_issue_pending(struct dma_chan *chan)
+{
+ struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
+ struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->lock, flags);
+ list_splice_tail_init(&c->desc_submitted, &c->desc_issued);
+ if (!list_empty(&c->desc_issued)) {
+ spin_lock(&d->lock);
+ if (!c->phy && list_empty(&c->node)) {
+ list_add_tail(&c->node, &d->chan_pending);
+ tasklet_schedule(&d->task);
+ dev_dbg(d->slave.dev, "vchan %p: issued\n", c);
+ }
+ spin_unlock(&d->lock);
+ } else
+ dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", c);
+ spin_unlock_irqrestore(&c->lock, flags);
+}
+
+static dma_cookie_t sa11x0_dma_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+ struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(tx->chan);
+ struct sa11x0_dma_desc *txd = to_sa11x0_dma_tx(tx);
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->lock, flags);
+ c->chan.cookie += 1;
+ if (c->chan.cookie < 0)
+ c->chan.cookie = 1;
+ txd->tx.cookie = c->chan.cookie;
+
+ list_add_tail(&txd->node, &c->desc_submitted);
+ spin_unlock_irqrestore(&c->lock, flags);
+
+ dev_dbg(tx->chan->device->dev, "vchan %p: txd %p[%x]: submitted\n",
+ c, txd, txd->tx.cookie);
+
+ return txd->tx.cookie;
+}
+
+static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
+ struct dma_chan *chan, struct scatterlist *sg, unsigned int sglen,
+ enum dma_transfer_direction dir, unsigned long flags)
+{
+ struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
+ struct sa11x0_dma_desc *txd;
+ struct scatterlist *sgent;
+ unsigned i, j = sglen;
+ size_t size = 0;
+
+ /* SA11x0 channels can only operate in their native direction */
+ if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) {
+ dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n",
+ c, c->ddar, dir);
+ return NULL;
+ }
+
+ /* Do not allow zero-sized txds */
+ if (sglen == 0)
+ return NULL;
+
+ for_each_sg(sg, sgent, sglen, i) {
+ dma_addr_t addr = sg_dma_address(sgent);
+ unsigned int len = sg_dma_len(sgent);
+
+ if (len > DMA_MAX_SIZE)
+ j += DIV_ROUND_UP(len, DMA_MAX_SIZE & ~DMA_ALIGN) - 1;
+ if (addr & DMA_ALIGN) {
+ dev_dbg(chan->device->dev, "vchan %p: bad buffer alignment: %08x\n",
+ c, addr);
+ return NULL;
+ }
+ }
+
+ txd = kzalloc(sizeof(*txd) + j * sizeof(txd->sg[0]), GFP_ATOMIC);
+ if (!txd) {
+ dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", c);
+ return NULL;
+ }
+
+ j = 0;
+ for_each_sg(sg, sgent, sglen, i) {
+ dma_addr_t addr = sg_dma_address(sgent);
+ unsigned len = sg_dma_len(sgent);
+
+ size += len;
+
+ do {
+ unsigned tlen = len;
+
+ /*
+ * Check whether the transfer will fit. If not, try
+ * to split the transfer up such that we end up with
+ * equal chunks - but make sure that we preserve the
+ * alignment. This avoids small segments.
+ */
+ if (tlen > DMA_MAX_SIZE) {
+ unsigned mult = DIV_ROUND_UP(tlen,
+ DMA_MAX_SIZE & ~DMA_ALIGN);
+
+ tlen = (tlen / mult) & ~DMA_ALIGN;
+ }
+
+ txd->sg[j].addr = addr;
+ txd->sg[j].len = tlen;
+
+ addr += tlen;
+ len -= tlen;
+ j++;
+ } while (len);
+ }
+
+ dma_async_tx_descriptor_init(&txd->tx, &c->chan);
+ txd->tx.flags = flags;
+ txd->tx.tx_submit = sa11x0_dma_tx_submit;
+ txd->ddar = c->ddar;
+ txd->size = size;
+ txd->sglen = j;
+
+ dev_dbg(chan->device->dev, "vchan %p: txd %p: size %u nr %u\n",
+ c, txd, txd->size, txd->sglen);
+
+ return &txd->tx;
+}
+
+static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg)
+{
+ u32 ddar = c->ddar & ((0xf << 4) | DDAR_RW);
+ dma_addr_t addr;
+ enum dma_slave_buswidth width;
+ u32 maxburst;
+
+ if (ddar & DDAR_RW) {
+ addr = cfg->src_addr;
+ width = cfg->src_addr_width;
+ maxburst = cfg->src_maxburst;
+ } else {
+ addr = cfg->dst_addr;
+ width = cfg->dst_addr_width;
+ maxburst = cfg->dst_maxburst;
+ }
+
+ if ((width != DMA_SLAVE_BUSWIDTH_1_BYTE &&
+ width != DMA_SLAVE_BUSWIDTH_2_BYTES) ||
+ (maxburst != 4 && maxburst != 8))
+ return -EINVAL;
+
+ if (width == DMA_SLAVE_BUSWIDTH_2_BYTES)
+ ddar |= DDAR_DW;
+ if (maxburst == 8)
+ ddar |= DDAR_BS;
+
+ dev_dbg(c->chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n",
+ c, addr, width, maxburst);
+
+ c->ddar = ddar | (addr & 0xf0000000) | (addr & 0x003ffffc) << 6;
+
+ return 0;
+}
+
+static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
+ struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
+ struct sa11x0_dma_phy *p;
+ LIST_HEAD(head);
+ unsigned long flags;
+ int ret;
+
+ switch (cmd) {
+ case DMA_SLAVE_CONFIG:
+ return sa11x0_dma_slave_config(c, (struct dma_slave_config *)arg);
+
+ case DMA_TERMINATE_ALL:
+ dev_dbg(d->slave.dev, "vchan %p: terminate all\n", c);
+ /* Clear the tx descriptor lists */
+ spin_lock_irqsave(&c->lock, flags);
+ list_splice_tail_init(&c->desc_submitted, &head);
+ list_splice_tail_init(&c->desc_issued, &head);
+
+ p = c->phy;
+ if (p) {
+ struct sa11x0_dma_desc *txd, *txn;
+
+ dev_dbg(d->slave.dev, "pchan %u: terminating\n", p->num);
+ /* vchan is assigned to a pchan - stop the channel */
+ writel(DCSR_RUN | DCSR_IE |
+ DCSR_STRTA | DCSR_DONEA |
+ DCSR_STRTB | DCSR_DONEB,
+ p->base + DMA_DCSR_C);
+
+ list_for_each_entry_safe(txd, txn, &d->desc_complete, node)
+ if (txd->tx.chan == &c->chan)
+ list_move(&txd->node, &head);
+
+ if (p->txd_load) {
+ if (p->txd_load != p->txd_done)
+ list_add_tail(&p->txd_load->node, &head);
+ p->txd_load = NULL;
+ }
+ if (p->txd_done) {
+ list_add_tail(&p->txd_done->node, &head);
+ p->txd_done = NULL;
+ }
+ c->phy = NULL;
+ spin_lock(&d->lock);
+ p->vchan = NULL;
+ spin_unlock(&d->lock);
+ tasklet_schedule(&d->task);
+ }
+ spin_unlock_irqrestore(&c->lock, flags);
+ sa11x0_dma_desc_free(d, &head);
+ ret = 0;
+ break;
+
+ case DMA_PAUSE:
+ dev_dbg(d->slave.dev, "vchan %p: pause\n", c);
+ spin_lock_irqsave(&c->lock, flags);
+ if (c->status == DMA_IN_PROGRESS) {
+ c->status = DMA_PAUSED;
+
+ p = c->phy;
+ if (p) {
+ writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_C);
+ } else {
+ spin_lock(&d->lock);
+ list_del_init(&c->node);
+ spin_unlock(&d->lock);
+ }
+ }
+ spin_unlock_irqrestore(&c->lock, flags);
+ ret = 0;
+ break;
+
+ case DMA_RESUME:
+ dev_dbg(d->slave.dev, "vchan %p: resume\n", c);
+ spin_lock_irqsave(&c->lock, flags);
+ if (c->status == DMA_PAUSED) {
+ c->status = DMA_IN_PROGRESS;
+
+ p = c->phy;
+ if (p) {
+ writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_S);
+ } else if (!list_empty(&c->desc_issued)) {
+ spin_lock(&d->lock);
+ list_add_tail(&c->node, &d->chan_pending);
+ spin_unlock(&d->lock);
+ }
+ }
+ spin_unlock_irqrestore(&c->lock, flags);
+ ret = 0;
+ break;
+
+ default:
+ ret = -ENXIO;
+ break;
+ }
+
+ return ret;
+}
+
+struct sa11x0_dma_channel_desc {
+ u32 ddar;
+ const char *name;
+};
+
+#define CD(d1, d2) { .ddar = DDAR_##d1 | d2, .name = #d1 }
+static const struct sa11x0_dma_channel_desc chan_desc[] = {
+ CD(Ser0UDCTr, 0),
+ CD(Ser0UDCRc, DDAR_RW),
+ CD(Ser1SDLCTr, 0),
+ CD(Ser1SDLCRc, DDAR_RW),
+ CD(Ser1UARTTr, 0),
+ CD(Ser1UARTRc, DDAR_RW),
+ CD(Ser2ICPTr, 0),
+ CD(Ser2ICPRc, DDAR_RW),
+ CD(Ser3UARTTr, 0),
+ CD(Ser3UARTRc, DDAR_RW),
+ CD(Ser4MCP0Tr, 0),
+ CD(Ser4MCP0Rc, DDAR_RW),
+ CD(Ser4MCP1Tr, 0),
+ CD(Ser4MCP1Rc, DDAR_RW),
+ CD(Ser4SSPTr, 0),
+ CD(Ser4SSPRc, DDAR_RW),
+};
+
+static int __devinit sa11x0_dma_init_dmadev(struct dma_device *dmadev,
+ struct device *dev)
+{
+ unsigned i;
+
+ dmadev->chancnt = ARRAY_SIZE(chan_desc);
+ INIT_LIST_HEAD(&dmadev->channels);
+ dmadev->dev = dev;
+ dmadev->device_alloc_chan_resources = sa11x0_dma_alloc_chan_resources;
+ dmadev->device_free_chan_resources = sa11x0_dma_free_chan_resources;
+ dmadev->device_control = sa11x0_dma_control;
+ dmadev->device_tx_status = sa11x0_dma_tx_status;
+ dmadev->device_issue_pending = sa11x0_dma_issue_pending;
+
+ for (i = 0; i < dmadev->chancnt; i++) {
+ struct sa11x0_dma_chan *c;
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ dev_err(dev, "no memory for channel %u\n", i);
+ return -ENOMEM;
+ }
+
+ c->chan.device = dmadev;
+ c->status = DMA_IN_PROGRESS;
+ c->ddar = chan_desc[i].ddar;
+ c->name = chan_desc[i].name;
+ spin_lock_init(&c->lock);
+ INIT_LIST_HEAD(&c->desc_submitted);
+ INIT_LIST_HEAD(&c->desc_issued);
+ INIT_LIST_HEAD(&c->node);
+ list_add_tail(&c->chan.device_node, &dmadev->channels);
+ }
+
+ return dma_async_device_register(dmadev);
+}
+
+static int sa11x0_dma_request_irq(struct platform_device *pdev, int nr,
+ void *data)
+{
+ int irq = platform_get_irq(pdev, nr);
+
+ if (irq <= 0)
+ return -ENXIO;
+
+ return request_irq(irq, sa11x0_dma_irq, 0, dev_name(&pdev->dev), data);
+}
+
+static void sa11x0_dma_free_irq(struct platform_device *pdev, int nr,
+ void *data)
+{
+ int irq = platform_get_irq(pdev, nr);
+ if (irq > 0)
+ free_irq(irq, data);
+}
+
+static void sa11x0_dma_free_channels(struct dma_device *dmadev)
+{
+ struct sa11x0_dma_chan *c, *cn;
+
+ list_for_each_entry_safe(c, cn, &dmadev->channels, chan.device_node) {
+ list_del(&c->chan.device_node);
+ kfree(c);
+ }
+}
+
+static int __devinit sa11x0_dma_probe(struct platform_device *pdev)
+{
+ struct sa11x0_dma_dev *d;
+ struct resource *res;
+ unsigned i;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ spin_lock_init(&d->lock);
+ INIT_LIST_HEAD(&d->chan_pending);
+ INIT_LIST_HEAD(&d->desc_complete);
+
+ d->base = ioremap(res->start, resource_size(res));
+ if (!d->base) {
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ tasklet_init(&d->task, sa11x0_dma_tasklet, (unsigned long)d);
+
+ for (i = 0; i < NR_PHY_CHAN; i++) {
+ struct sa11x0_dma_phy *p = &d->phy[i];
+
+ p->dev = d;
+ p->num = i;
+ p->base = d->base + i * DMA_SIZE;
+ writel_relaxed(DCSR_RUN | DCSR_IE | DCSR_ERROR |
+ DCSR_DONEA | DCSR_STRTA | DCSR_DONEB | DCSR_STRTB,
+ p->base + DMA_DCSR_C);
+ writel_relaxed(0, p->base + DMA_DDAR);
+
+ ret = sa11x0_dma_request_irq(pdev, i, p);
+ if (ret) {
+ while (i) {
+ i--;
+ sa11x0_dma_free_irq(pdev, i, &d->phy[i]);
+ }
+ goto err_irq;
+ }
+ }
+
+ dma_cap_set(DMA_SLAVE, d->slave.cap_mask);
+ d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg;
+ ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev);
+ if (ret) {
+ dev_warn(d->slave.dev, "failed to register slave async device: %d\n",
+ ret);
+ goto err_slave_reg;
+ }
+
+ platform_set_drvdata(pdev, d);
+ return 0;
+
+ err_slave_reg:
+ sa11x0_dma_free_channels(&d->slave);
+ for (i = 0; i < NR_PHY_CHAN; i++)
+ sa11x0_dma_free_irq(pdev, i, &d->phy[i]);
+ err_irq:
+ tasklet_kill(&d->task);
+ iounmap(d->base);
+ err_ioremap:
+ kfree(d);
+ err_alloc:
+ return ret;
+}
+
+static int __devexit sa11x0_dma_remove(struct platform_device *pdev)
+{
+ struct sa11x0_dma_dev *d = platform_get_drvdata(pdev);
+ unsigned pch;
+
+ dma_async_device_unregister(&d->slave);
+
+ sa11x0_dma_free_channels(&d->slave);
+ for (pch = 0; pch < NR_PHY_CHAN; pch++)
+ sa11x0_dma_free_irq(pdev, pch, &d->phy[pch]);
+ tasklet_kill(&d->task);
+ iounmap(d->base);
+ kfree(d);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sa11x0_dma_suspend(struct device *dev)
+{
+ struct sa11x0_dma_dev *d = dev_get_drvdata(dev);
+ unsigned pch;
+
+ for (pch = 0; pch < NR_PHY_CHAN; pch++) {
+ struct sa11x0_dma_phy *p = &d->phy[pch];
+ u32 dcsr, saved_dcsr;
+
+ dcsr = saved_dcsr = readl_relaxed(p->base + DMA_DCSR_R);
+ if (dcsr & DCSR_RUN) {
+ writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_C);
+ dcsr = readl_relaxed(p->base + DMA_DCSR_R);
+ }
+
+ saved_dcsr &= DCSR_RUN | DCSR_IE;
+ if (dcsr & DCSR_BIU) {
+ p->dbs[0] = readl_relaxed(p->base + DMA_DBSB);
+ p->dbt[0] = readl_relaxed(p->base + DMA_DBTB);
+ p->dbs[1] = readl_relaxed(p->base + DMA_DBSA);
+ p->dbt[1] = readl_relaxed(p->base + DMA_DBTA);
+ saved_dcsr |= (dcsr & DCSR_STRTA ? DCSR_STRTB : 0) |
+ (dcsr & DCSR_STRTB ? DCSR_STRTA : 0);
+ } else {
+ p->dbs[0] = readl_relaxed(p->base + DMA_DBSA);
+ p->dbt[0] = readl_relaxed(p->base + DMA_DBTA);
+ p->dbs[1] = readl_relaxed(p->base + DMA_DBSB);
+ p->dbt[1] = readl_relaxed(p->base + DMA_DBTB);
+ saved_dcsr |= dcsr & (DCSR_STRTA | DCSR_STRTB);
+ }
+ p->dcsr = saved_dcsr;
+
+ writel(DCSR_STRTA | DCSR_STRTB, p->base + DMA_DCSR_C);
+ }
+
+ return 0;
+}
+
+static int sa11x0_dma_resume(struct device *dev)
+{
+ struct sa11x0_dma_dev *d = dev_get_drvdata(dev);
+ unsigned pch;
+
+ for (pch = 0; pch < NR_PHY_CHAN; pch++) {
+ struct sa11x0_dma_phy *p = &d->phy[pch];
+ struct sa11x0_dma_desc *txd = NULL;
+ u32 dcsr = readl_relaxed(p->base + DMA_DCSR_R);
+
+ WARN_ON(dcsr & (DCSR_BIU | DCSR_STRTA | DCSR_STRTB | DCSR_RUN));
+
+ if (p->txd_done)
+ txd = p->txd_done;
+ else if (p->txd_load)
+ txd = p->txd_load;
+
+ if (!txd)
+ continue;
+
+ writel_relaxed(txd->ddar, p->base + DMA_DDAR);
+
+ writel_relaxed(p->dbs[0], p->base + DMA_DBSA);
+ writel_relaxed(p->dbt[0], p->base + DMA_DBTA);
+ writel_relaxed(p->dbs[1], p->base + DMA_DBSB);
+ writel_relaxed(p->dbt[1], p->base + DMA_DBTB);
+ writel_relaxed(p->dcsr, p->base + DMA_DCSR_S);
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops sa11x0_dma_pm_ops = {
+ .suspend_noirq = sa11x0_dma_suspend,
+ .resume_noirq = sa11x0_dma_resume,
+ .freeze_noirq = sa11x0_dma_suspend,
+ .thaw_noirq = sa11x0_dma_resume,
+ .poweroff_noirq = sa11x0_dma_suspend,
+ .restore_noirq = sa11x0_dma_resume,
+};
+
+static struct platform_driver sa11x0_dma_driver = {
+ .driver = {
+ .name = "sa11x0-dma",
+ .owner = THIS_MODULE,
+ .pm = &sa11x0_dma_pm_ops,
+ },
+ .probe = sa11x0_dma_probe,
+ .remove = __devexit_p(sa11x0_dma_remove),
+};
+
+bool sa11x0_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+ if (chan->device->dev->driver == &sa11x0_dma_driver.driver) {
+ struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
+ const char *p = param;
+
+ return !strcmp(c->name, p);
+ }
+ return false;
+}
+EXPORT_SYMBOL(sa11x0_dma_filter_fn);
+
+static int __init sa11x0_dma_init(void)
+{
+ return platform_driver_register(&sa11x0_dma_driver);
+}
+subsys_initcall(sa11x0_dma_init);
+
+static void __exit sa11x0_dma_exit(void)
+{
+ platform_driver_unregister(&sa11x0_dma_driver);
+}
+module_exit(sa11x0_dma_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("SA-11x0 DMA driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sa11x0-dma");
diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
index 812fd76..19d7a8d 100644
--- a/drivers/dma/shdma.c
+++ b/drivers/dma/shdma.c
@@ -30,6 +30,8 @@
#include <linux/kdebug.h>
#include <linux/spinlock.h>
#include <linux/rculist.h>
+
+#include "dmaengine.h"
#include "shdma.h"
/* DMA descriptor control */
@@ -296,13 +298,7 @@ static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx)
else
power_up = false;
- cookie = sh_chan->common.cookie;
- cookie++;
- if (cookie < 0)
- cookie = 1;
-
- sh_chan->common.cookie = cookie;
- tx->cookie = cookie;
+ cookie = dma_cookie_assign(tx);
/* Mark all chunks of this descriptor as submitted, move to the queue */
list_for_each_entry_safe(chunk, c, desc->node.prev, node) {
@@ -673,7 +669,8 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_memcpy(
static struct dma_async_tx_descriptor *sh_dmae_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
- enum dma_transfer_direction direction, unsigned long flags)
+ enum dma_transfer_direction direction, unsigned long flags,
+ void *context)
{
struct sh_dmae_slave *param;
struct sh_dmae_chan *sh_chan;
@@ -764,12 +761,12 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all
cookie = tx->cookie;
if (desc->mark == DESC_COMPLETED && desc->chunks == 1) {
- if (sh_chan->completed_cookie != desc->cookie - 1)
+ if (sh_chan->common.completed_cookie != desc->cookie - 1)
dev_dbg(sh_chan->dev,
"Completing cookie %d, expected %d\n",
desc->cookie,
- sh_chan->completed_cookie + 1);
- sh_chan->completed_cookie = desc->cookie;
+ sh_chan->common.completed_cookie + 1);
+ sh_chan->common.completed_cookie = desc->cookie;
}
/* Call callback on the last chunk */
@@ -823,7 +820,7 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all
* Terminating and the loop completed normally: forgive
* uncompleted cookies
*/
- sh_chan->completed_cookie = sh_chan->common.cookie;
+ sh_chan->common.completed_cookie = sh_chan->common.cookie;
spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
@@ -883,23 +880,14 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan,
struct dma_tx_state *txstate)
{
struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
enum dma_status status;
unsigned long flags;
sh_dmae_chan_ld_cleanup(sh_chan, false);
- /* First read completed cookie to avoid a skew */
- last_complete = sh_chan->completed_cookie;
- rmb();
- last_used = chan->cookie;
- BUG_ON(last_complete < 0);
- dma_set_tx_state(txstate, last_complete, last_used, 0);
-
spin_lock_irqsave(&sh_chan->desc_lock, flags);
- status = dma_async_is_complete(cookie, last_complete, last_used);
+ status = dma_cookie_status(chan, cookie, txstate);
/*
* If we don't find cookie on the queue, it has been aborted and we have
@@ -1102,6 +1090,7 @@ static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id,
/* reference struct dma_device */
new_sh_chan->common.device = &shdev->common;
+ dma_cookie_init(&new_sh_chan->common);
new_sh_chan->dev = shdev->common.dev;
new_sh_chan->id = id;
diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h
index 2b55a27..0b1d2c1 100644
--- a/drivers/dma/shdma.h
+++ b/drivers/dma/shdma.h
@@ -30,7 +30,6 @@ enum dmae_pm_state {
};
struct sh_dmae_chan {
- dma_cookie_t completed_cookie; /* The maximum cookie completed */
spinlock_t desc_lock; /* Descriptor operation lock */
struct list_head ld_queue; /* Link descriptors queue */
struct list_head ld_free; /* Link descriptors free */
diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c
index 2333810..434ad31 100644
--- a/drivers/dma/sirf-dma.c
+++ b/drivers/dma/sirf-dma.c
@@ -18,6 +18,8 @@
#include <linux/of_platform.h>
#include <linux/sirfsoc_dma.h>
+#include "dmaengine.h"
+
#define SIRFSOC_DMA_DESCRIPTORS 16
#define SIRFSOC_DMA_CHANNELS 16
@@ -59,7 +61,6 @@ struct sirfsoc_dma_chan {
struct list_head queued;
struct list_head active;
struct list_head completed;
- dma_cookie_t completed_cookie;
unsigned long happened_cyclic;
unsigned long completed_cyclic;
@@ -208,7 +209,7 @@ static void sirfsoc_dma_process_completed(struct sirfsoc_dma *sdma)
/* Free descriptors */
spin_lock_irqsave(&schan->lock, flags);
list_splice_tail_init(&list, &schan->free);
- schan->completed_cookie = last_cookie;
+ schan->chan.completed_cookie = last_cookie;
spin_unlock_irqrestore(&schan->lock, flags);
} else {
/* for cyclic channel, desc is always in active list */
@@ -258,13 +259,7 @@ static dma_cookie_t sirfsoc_dma_tx_submit(struct dma_async_tx_descriptor *txd)
/* Move descriptor to queue */
list_move_tail(&sdesc->node, &schan->queued);
- /* Update cookie */
- cookie = schan->chan.cookie + 1;
- if (cookie <= 0)
- cookie = 1;
-
- schan->chan.cookie = cookie;
- sdesc->desc.cookie = cookie;
+ cookie = dma_cookie_assign(txd);
spin_unlock_irqrestore(&schan->lock, flags);
@@ -414,16 +409,13 @@ sirfsoc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
{
struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan);
unsigned long flags;
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
+ enum dma_status ret;
spin_lock_irqsave(&schan->lock, flags);
- last_used = schan->chan.cookie;
- last_complete = schan->completed_cookie;
+ ret = dma_cookie_status(chan, cookie, txstate);
spin_unlock_irqrestore(&schan->lock, flags);
- dma_set_tx_state(txstate, last_complete, last_used, 0);
- return dma_async_is_complete(cookie, last_complete, last_used);
+ return ret;
}
static struct dma_async_tx_descriptor *sirfsoc_dma_prep_interleaved(
@@ -497,7 +489,7 @@ err_dir:
static struct dma_async_tx_descriptor *
sirfsoc_dma_prep_cyclic(struct dma_chan *chan, dma_addr_t addr,
size_t buf_len, size_t period_len,
- enum dma_transfer_direction direction)
+ enum dma_transfer_direction direction, void *context)
{
struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan);
struct sirfsoc_dma_desc *sdesc = NULL;
@@ -635,8 +627,7 @@ static int __devinit sirfsoc_dma_probe(struct platform_device *op)
schan = &sdma->channels[i];
schan->chan.device = dma;
- schan->chan.cookie = 1;
- schan->completed_cookie = schan->chan.cookie;
+ dma_cookie_init(&schan->chan);
INIT_LIST_HEAD(&schan->free);
INIT_LIST_HEAD(&schan->prepared);
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index cc5ecbc..bdd41d4 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -21,6 +21,7 @@
#include <plat/ste_dma40.h>
+#include "dmaengine.h"
#include "ste_dma40_ll.h"
#define D40_NAME "dma40"
@@ -220,8 +221,6 @@ struct d40_base;
*
* @lock: A spinlock to protect this struct.
* @log_num: The logical number, if any of this channel.
- * @completed: Starts with 1, after first interrupt it is set to dma engine's
- * current cookie.
* @pending_tx: The number of pending transfers. Used between interrupt handler
* and tasklet.
* @busy: Set to true when transfer is ongoing on this channel.
@@ -250,8 +249,6 @@ struct d40_base;
struct d40_chan {
spinlock_t lock;
int log_num;
- /* ID of the most recent completed transfer */
- int completed;
int pending_tx;
bool busy;
struct d40_phy_res *phy_chan;
@@ -1223,21 +1220,14 @@ static dma_cookie_t d40_tx_submit(struct dma_async_tx_descriptor *tx)
chan);
struct d40_desc *d40d = container_of(tx, struct d40_desc, txd);
unsigned long flags;
+ dma_cookie_t cookie;
spin_lock_irqsave(&d40c->lock, flags);
-
- d40c->chan.cookie++;
-
- if (d40c->chan.cookie < 0)
- d40c->chan.cookie = 1;
-
- d40d->txd.cookie = d40c->chan.cookie;
-
+ cookie = dma_cookie_assign(tx);
d40_desc_queue(d40c, d40d);
-
spin_unlock_irqrestore(&d40c->lock, flags);
- return tx->cookie;
+ return cookie;
}
static int d40_start(struct d40_chan *d40c)
@@ -1357,7 +1347,7 @@ static void dma_tasklet(unsigned long data)
goto err;
if (!d40d->cyclic)
- d40c->completed = d40d->txd.cookie;
+ dma_cookie_complete(&d40d->txd);
/*
* If terminating a channel pending_tx is set to zero.
@@ -2182,7 +2172,7 @@ static int d40_alloc_chan_resources(struct dma_chan *chan)
bool is_free_phy;
spin_lock_irqsave(&d40c->lock, flags);
- d40c->completed = chan->cookie = 1;
+ dma_cookie_init(chan);
/* If no dma configuration is set use default configuration (memcpy) */
if (!d40c->configured) {
@@ -2299,7 +2289,8 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan,
struct scatterlist *sgl,
unsigned int sg_len,
enum dma_transfer_direction direction,
- unsigned long dma_flags)
+ unsigned long dma_flags,
+ void *context)
{
if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV)
return NULL;
@@ -2310,7 +2301,7 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan,
static struct dma_async_tx_descriptor *
dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
size_t buf_len, size_t period_len,
- enum dma_transfer_direction direction)
+ enum dma_transfer_direction direction, void *context)
{
unsigned int periods = buf_len / period_len;
struct dma_async_tx_descriptor *txd;
@@ -2342,25 +2333,19 @@ static enum dma_status d40_tx_status(struct dma_chan *chan,
struct dma_tx_state *txstate)
{
struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
- int ret;
+ enum dma_status ret;
if (d40c->phy_chan == NULL) {
chan_err(d40c, "Cannot read status of unallocated channel\n");
return -EINVAL;
}
- last_complete = d40c->completed;
- last_used = chan->cookie;
+ ret = dma_cookie_status(chan, cookie, txstate);
+ if (ret != DMA_SUCCESS)
+ dma_set_residue(txstate, stedma40_residue(chan));
if (d40_is_paused(d40c))
ret = DMA_PAUSED;
- else
- ret = dma_async_is_complete(cookie, last_complete, last_used);
-
- dma_set_tx_state(txstate, last_complete, last_used,
- stedma40_residue(chan));
return ret;
}
diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c
index a6f9c16..4e0dff5 100644
--- a/drivers/dma/timb_dma.c
+++ b/drivers/dma/timb_dma.c
@@ -31,6 +31,8 @@
#include <linux/timb_dma.h>
+#include "dmaengine.h"
+
#define DRIVER_NAME "timb-dma"
/* Global DMA registers */
@@ -84,7 +86,6 @@ struct timb_dma_chan {
especially the lists and descriptors,
from races between the tasklet and calls
from above */
- dma_cookie_t last_completed_cookie;
bool ongoing;
struct list_head active_list;
struct list_head queue;
@@ -284,7 +285,7 @@ static void __td_finish(struct timb_dma_chan *td_chan)
else
iowrite32(0, td_chan->membase + TIMBDMA_OFFS_TX_DLAR);
*/
- td_chan->last_completed_cookie = txd->cookie;
+ dma_cookie_complete(txd);
td_chan->ongoing = false;
callback = txd->callback;
@@ -349,12 +350,7 @@ static dma_cookie_t td_tx_submit(struct dma_async_tx_descriptor *txd)
dma_cookie_t cookie;
spin_lock_bh(&td_chan->lock);
-
- cookie = txd->chan->cookie;
- if (++cookie < 0)
- cookie = 1;
- txd->chan->cookie = cookie;
- txd->cookie = cookie;
+ cookie = dma_cookie_assign(txd);
if (list_empty(&td_chan->active_list)) {
dev_dbg(chan2dev(txd->chan), "%s: started %u\n", __func__,
@@ -481,8 +477,7 @@ static int td_alloc_chan_resources(struct dma_chan *chan)
}
spin_lock_bh(&td_chan->lock);
- td_chan->last_completed_cookie = 1;
- chan->cookie = 1;
+ dma_cookie_init(chan);
spin_unlock_bh(&td_chan->lock);
return 0;
@@ -515,24 +510,13 @@ static void td_free_chan_resources(struct dma_chan *chan)
static enum dma_status td_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
- struct timb_dma_chan *td_chan =
- container_of(chan, struct timb_dma_chan, chan);
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
- int ret;
+ enum dma_status ret;
dev_dbg(chan2dev(chan), "%s: Entry\n", __func__);
- last_complete = td_chan->last_completed_cookie;
- last_used = chan->cookie;
-
- ret = dma_async_is_complete(cookie, last_complete, last_used);
-
- dma_set_tx_state(txstate, last_complete, last_used, 0);
+ ret = dma_cookie_status(chan, cookie, txstate);
- dev_dbg(chan2dev(chan),
- "%s: exit, ret: %d, last_complete: %d, last_used: %d\n",
- __func__, ret, last_complete, last_used);
+ dev_dbg(chan2dev(chan), "%s: exit, ret: %d\n", __func__, ret);
return ret;
}
@@ -558,7 +542,8 @@ static void td_issue_pending(struct dma_chan *chan)
static struct dma_async_tx_descriptor *td_prep_slave_sg(struct dma_chan *chan,
struct scatterlist *sgl, unsigned int sg_len,
- enum dma_transfer_direction direction, unsigned long flags)
+ enum dma_transfer_direction direction, unsigned long flags,
+ void *context)
{
struct timb_dma_chan *td_chan =
container_of(chan, struct timb_dma_chan, chan);
@@ -766,7 +751,7 @@ static int __devinit td_probe(struct platform_device *pdev)
}
td_chan->chan.device = &td->dma;
- td_chan->chan.cookie = 1;
+ dma_cookie_init(&td_chan->chan);
spin_lock_init(&td_chan->lock);
INIT_LIST_HEAD(&td_chan->active_list);
INIT_LIST_HEAD(&td_chan->queue);
diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c
index 6122c36..913f55c 100644
--- a/drivers/dma/txx9dmac.c
+++ b/drivers/dma/txx9dmac.c
@@ -15,6 +15,8 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/scatterlist.h>
+
+#include "dmaengine.h"
#include "txx9dmac.h"
static struct txx9dmac_chan *to_txx9dmac_chan(struct dma_chan *chan)
@@ -279,21 +281,6 @@ static void txx9dmac_desc_put(struct txx9dmac_chan *dc,
}
}
-/* Called with dc->lock held and bh disabled */
-static dma_cookie_t
-txx9dmac_assign_cookie(struct txx9dmac_chan *dc, struct txx9dmac_desc *desc)
-{
- dma_cookie_t cookie = dc->chan.cookie;
-
- if (++cookie < 0)
- cookie = 1;
-
- dc->chan.cookie = cookie;
- desc->txd.cookie = cookie;
-
- return cookie;
-}
-
/*----------------------------------------------------------------------*/
static void txx9dmac_dump_regs(struct txx9dmac_chan *dc)
@@ -424,7 +411,7 @@ txx9dmac_descriptor_complete(struct txx9dmac_chan *dc,
dev_vdbg(chan2dev(&dc->chan), "descriptor %u %p complete\n",
txd->cookie, desc);
- dc->completed = txd->cookie;
+ dma_cookie_complete(txd);
callback = txd->callback;
param = txd->callback_param;
@@ -738,7 +725,7 @@ static dma_cookie_t txx9dmac_tx_submit(struct dma_async_tx_descriptor *tx)
dma_cookie_t cookie;
spin_lock_bh(&dc->lock);
- cookie = txx9dmac_assign_cookie(dc, desc);
+ cookie = dma_cookie_assign(tx);
dev_vdbg(chan2dev(tx->chan), "tx_submit: queued %u %p\n",
desc->txd.cookie, desc);
@@ -846,7 +833,7 @@ txx9dmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
static struct dma_async_tx_descriptor *
txx9dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
- unsigned long flags)
+ unsigned long flags, void *context)
{
struct txx9dmac_chan *dc = to_txx9dmac_chan(chan);
struct txx9dmac_dev *ddev = dc->ddev;
@@ -972,27 +959,17 @@ txx9dmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
struct txx9dmac_chan *dc = to_txx9dmac_chan(chan);
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
- int ret;
+ enum dma_status ret;
- last_complete = dc->completed;
- last_used = chan->cookie;
-
- ret = dma_async_is_complete(cookie, last_complete, last_used);
+ ret = dma_cookie_status(chan, cookie, txstate);
if (ret != DMA_SUCCESS) {
spin_lock_bh(&dc->lock);
txx9dmac_scan_descriptors(dc);
spin_unlock_bh(&dc->lock);
- last_complete = dc->completed;
- last_used = chan->cookie;
-
- ret = dma_async_is_complete(cookie, last_complete, last_used);
+ ret = dma_cookie_status(chan, cookie, txstate);
}
- dma_set_tx_state(txstate, last_complete, last_used, 0);
-
return ret;
}
@@ -1057,7 +1034,7 @@ static int txx9dmac_alloc_chan_resources(struct dma_chan *chan)
return -EIO;
}
- dc->completed = chan->cookie = 1;
+ dma_cookie_init(chan);
dc->ccr = TXX9_DMA_CCR_IMMCHN | TXX9_DMA_CCR_INTENE | CCR_LE;
txx9dmac_chan_set_SMPCHN(dc);
@@ -1186,7 +1163,7 @@ static int __init txx9dmac_chan_probe(struct platform_device *pdev)
dc->ddev->chan[ch] = dc;
dc->chan.device = &dc->dma;
list_add_tail(&dc->chan.device_node, &dc->chan.device->channels);
- dc->chan.cookie = dc->completed = 1;
+ dma_cookie_init(&dc->chan);
if (is_dmac64(dc))
dc->ch_regs = &__txx9dmac_regs(dc->ddev)->CHAN[ch];
diff --git a/drivers/dma/txx9dmac.h b/drivers/dma/txx9dmac.h
index 365d423..f5a7605 100644
--- a/drivers/dma/txx9dmac.h
+++ b/drivers/dma/txx9dmac.h
@@ -172,7 +172,6 @@ struct txx9dmac_chan {
spinlock_t lock;
/* these other elements are all protected by lock */
- dma_cookie_t completed;
struct list_head active_list;
struct list_head queue;
struct list_head free_list;
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 5948a21..fdffa1b 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -215,7 +215,7 @@ config EDAC_I7300
config EDAC_SBRIDGE
tristate "Intel Sandy-Bridge Integrated MC"
depends on EDAC_MM_EDAC && PCI && X86_64 && X86_MCE_INTEL
- depends on EXPERIMENTAL
+ depends on PCI_MMCONFIG && EXPERIMENTAL
help
Support for error detection and correction the Intel
Sandy Bridge Integrated Memory Controller.
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index da09cd7..feef773 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -39,7 +39,7 @@ static LIST_HEAD(mc_devices);
#ifdef CONFIG_EDAC_DEBUG
-static void edac_mc_dump_channel(struct channel_info *chan)
+static void edac_mc_dump_channel(struct rank_info *chan)
{
debugf4("\tchannel = %p\n", chan);
debugf4("\tchannel->chan_idx = %d\n", chan->chan_idx);
@@ -156,7 +156,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
{
struct mem_ctl_info *mci;
struct csrow_info *csi, *csrow;
- struct channel_info *chi, *chp, *chan;
+ struct rank_info *chi, *chp, *chan;
void *pvt;
unsigned size;
int row, chn;
@@ -181,7 +181,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
* rather than an imaginary chunk of memory located at address 0.
*/
csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi));
- chi = (struct channel_info *)(((char *)mci) + ((unsigned long)chi));
+ chi = (struct rank_info *)(((char *)mci) + ((unsigned long)chi));
pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL;
/* setup index and various internal pointers */
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c
index 2e23547..d500749 100644
--- a/drivers/edac/i5100_edac.c
+++ b/drivers/edac/i5100_edac.c
@@ -49,7 +49,7 @@
#define I5100_FERR_NF_MEM_M6ERR_MASK (1 << 6)
#define I5100_FERR_NF_MEM_M5ERR_MASK (1 << 5)
#define I5100_FERR_NF_MEM_M4ERR_MASK (1 << 4)
-#define I5100_FERR_NF_MEM_M1ERR_MASK 1
+#define I5100_FERR_NF_MEM_M1ERR_MASK (1 << 1)
#define I5100_FERR_NF_MEM_ANY_MASK \
(I5100_FERR_NF_MEM_M16ERR_MASK | \
I5100_FERR_NF_MEM_M15ERR_MASK | \
@@ -535,23 +535,20 @@ static void i5100_read_log(struct mem_ctl_info *mci, int chan,
static void i5100_check_error(struct mem_ctl_info *mci)
{
struct i5100_priv *priv = mci->pvt_info;
- u32 dw;
-
+ u32 dw, dw2;
pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw);
if (i5100_ferr_nf_mem_any(dw)) {
- u32 dw2;
pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2);
- if (dw2)
- pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM,
- dw2);
- pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw);
i5100_read_log(mci, i5100_ferr_nf_mem_chan_indx(dw),
i5100_ferr_nf_mem_any(dw),
i5100_nerr_nf_mem_any(dw2));
+
+ pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM, dw2);
}
+ pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw);
}
/* The i5100 chipset will scrub the entire memory once, then
diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c
index 67ec962..1869a10 100644
--- a/drivers/edac/i5400_edac.c
+++ b/drivers/edac/i5400_edac.c
@@ -735,7 +735,7 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
/* Attempt to 'get' the MCH register we want */
pdev = NULL;
- while (!pvt->branchmap_werrors || !pvt->fsb_error_regs) {
+ while (1) {
pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_5400_ERR, pdev);
if (!pdev) {
@@ -743,23 +743,42 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
i5400_printk(KERN_ERR,
"'system address,Process Bus' "
"device not found:"
- "vendor 0x%x device 0x%x ERR funcs "
+ "vendor 0x%x device 0x%x ERR func 1 "
"(broken BIOS?)\n",
PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_5400_ERR);
- goto error;
+ return -ENODEV;
}
- /* Store device 16 funcs 1 and 2 */
- switch (PCI_FUNC(pdev->devfn)) {
- case 1:
- pvt->branchmap_werrors = pdev;
- break;
- case 2:
- pvt->fsb_error_regs = pdev;
+ /* Store device 16 func 1 */
+ if (PCI_FUNC(pdev->devfn) == 1)
break;
+ }
+ pvt->branchmap_werrors = pdev;
+
+ pdev = NULL;
+ while (1) {
+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_5400_ERR, pdev);
+ if (!pdev) {
+ /* End of list, leave */
+ i5400_printk(KERN_ERR,
+ "'system address,Process Bus' "
+ "device not found:"
+ "vendor 0x%x device 0x%x ERR func 2 "
+ "(broken BIOS?)\n",
+ PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_5400_ERR);
+
+ pci_dev_put(pvt->branchmap_werrors);
+ return -ENODEV;
}
+
+ /* Store device 16 func 2 */
+ if (PCI_FUNC(pdev->devfn) == 2)
+ break;
}
+ pvt->fsb_error_regs = pdev;
debugf1("System Address, processor bus- PCI Bus ID: %s %x:%x\n",
pci_name(pvt->system_address),
@@ -778,7 +797,10 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
"MC: 'BRANCH 0' device not found:"
"vendor 0x%x device 0x%x Func 0 (broken BIOS?)\n",
PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_FBD0);
- goto error;
+
+ pci_dev_put(pvt->fsb_error_regs);
+ pci_dev_put(pvt->branchmap_werrors);
+ return -ENODEV;
}
/* If this device claims to have more than 2 channels then
@@ -796,14 +818,14 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
"(broken BIOS?)\n",
PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_5400_FBD1);
- goto error;
+
+ pci_dev_put(pvt->branch_0);
+ pci_dev_put(pvt->fsb_error_regs);
+ pci_dev_put(pvt->branchmap_werrors);
+ return -ENODEV;
}
return 0;
-
-error:
- i5400_put_devices(mci);
- return -ENODEV;
}
/*
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
index fc75706..d427c69 100644
--- a/drivers/edac/ppc4xx_edac.c
+++ b/drivers/edac/ppc4xx_edac.c
@@ -184,7 +184,7 @@ struct ppc4xx_ecc_status {
/* Function Prototypes */
-static int ppc4xx_edac_probe(struct platform_device *device)
+static int ppc4xx_edac_probe(struct platform_device *device);
static int ppc4xx_edac_remove(struct platform_device *device);
/* Global Variables */
@@ -1068,7 +1068,7 @@ ppc4xx_edac_mc_init(struct mem_ctl_info *mci,
mci->mod_name = PPC4XX_EDAC_MODULE_NAME;
mci->mod_ver = PPC4XX_EDAC_MODULE_REVISION;
- mci->ctl_name = match->compatible,
+ mci->ctl_name = ppc4xx_edac_match->compatible,
mci->dev_name = np->full_name;
/* Initialize callbacks */
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 3a605f7..a203536 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -20,6 +20,7 @@
#include <linux/mmzone.h>
#include <linux/smp.h>
#include <linux/bitmap.h>
+#include <linux/math64.h>
#include <asm/processor.h>
#include <asm/mce.h>
@@ -670,6 +671,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
u32 reg;
u64 limit, prv = 0;
u64 tmp_mb;
+ u32 mb, kb;
u32 rir_way;
/*
@@ -682,8 +684,9 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
pvt->tolm = GET_TOLM(reg);
tmp_mb = (1 + pvt->tolm) >> 20;
- debugf0("TOLM: %Lu.%03Lu GB (0x%016Lx)\n",
- tmp_mb / 1000, tmp_mb % 1000, (u64)pvt->tolm);
+ mb = div_u64_rem(tmp_mb, 1000, &kb);
+ debugf0("TOLM: %u.%03u GB (0x%016Lx)\n",
+ mb, kb, (u64)pvt->tolm);
/* Address range is already 45:25 */
pci_read_config_dword(pvt->pci_sad1, TOHM,
@@ -691,8 +694,9 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
pvt->tohm = GET_TOHM(reg);
tmp_mb = (1 + pvt->tohm) >> 20;
- debugf0("TOHM: %Lu.%03Lu GB (0x%016Lx)",
- tmp_mb / 1000, tmp_mb % 1000, (u64)pvt->tohm);
+ mb = div_u64_rem(tmp_mb, 1000, &kb);
+ debugf0("TOHM: %u.%03u GB (0x%016Lx)",
+ mb, kb, (u64)pvt->tohm);
/*
* Step 2) Get SAD range and SAD Interleave list
@@ -714,10 +718,11 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
break;
tmp_mb = (limit + 1) >> 20;
- debugf0("SAD#%d %s up to %Lu.%03Lu GB (0x%016Lx) %s reg=0x%08x\n",
+ mb = div_u64_rem(tmp_mb, 1000, &kb);
+ debugf0("SAD#%d %s up to %u.%03u GB (0x%016Lx) %s reg=0x%08x\n",
n_sads,
get_dram_attr(reg),
- tmp_mb / 1000, tmp_mb % 1000,
+ mb, kb,
((u64)tmp_mb) << 20L,
INTERLEAVE_MODE(reg) ? "Interleave: 8:6" : "Interleave: [8:6]XOR[18:16]",
reg);
@@ -747,8 +752,9 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
break;
tmp_mb = (limit + 1) >> 20;
- debugf0("TAD#%d: up to %Lu.%03Lu GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n",
- n_tads, tmp_mb / 1000, tmp_mb % 1000,
+ mb = div_u64_rem(tmp_mb, 1000, &kb);
+ debugf0("TAD#%d: up to %u.%03u GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n",
+ n_tads, mb, kb,
((u64)tmp_mb) << 20L,
(u32)TAD_SOCK(reg),
(u32)TAD_CH(reg),
@@ -757,7 +763,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
(u32)TAD_TGT2(reg),
(u32)TAD_TGT3(reg),
reg);
- prv = tmp_mb;
+ prv = limit;
}
/*
@@ -771,9 +777,10 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
tad_ch_nilv_offset[j],
&reg);
tmp_mb = TAD_OFFSET(reg) >> 20;
- debugf0("TAD CH#%d, offset #%d: %Lu.%03Lu GB (0x%016Lx), reg=0x%08x\n",
+ mb = div_u64_rem(tmp_mb, 1000, &kb);
+ debugf0("TAD CH#%d, offset #%d: %u.%03u GB (0x%016Lx), reg=0x%08x\n",
i, j,
- tmp_mb / 1000, tmp_mb % 1000,
+ mb, kb,
((u64)tmp_mb) << 20L,
reg);
}
@@ -795,9 +802,10 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
tmp_mb = RIR_LIMIT(reg) >> 20;
rir_way = 1 << RIR_WAY(reg);
- debugf0("CH#%d RIR#%d, limit: %Lu.%03Lu GB (0x%016Lx), way: %d, reg=0x%08x\n",
+ mb = div_u64_rem(tmp_mb, 1000, &kb);
+ debugf0("CH#%d RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d, reg=0x%08x\n",
i, j,
- tmp_mb / 1000, tmp_mb % 1000,
+ mb, kb,
((u64)tmp_mb) << 20L,
rir_way,
reg);
@@ -808,9 +816,10 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
&reg);
tmp_mb = RIR_OFFSET(reg) << 6;
- debugf0("CH#%d RIR#%d INTL#%d, offset %Lu.%03Lu GB (0x%016Lx), tgt: %d, reg=0x%08x\n",
+ mb = div_u64_rem(tmp_mb, 1000, &kb);
+ debugf0("CH#%d RIR#%d INTL#%d, offset %u.%03u GB (0x%016Lx), tgt: %d, reg=0x%08x\n",
i, j, k,
- tmp_mb / 1000, tmp_mb % 1000,
+ mb, kb,
((u64)tmp_mb) << 20L,
(u32)RIR_RNK_TGT(reg),
reg);
@@ -848,6 +857,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
u8 ch_way,sck_way;
u32 tad_offset;
u32 rir_way;
+ u32 mb, kb;
u64 ch_addr, offset, limit, prv = 0;
@@ -858,7 +868,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
* range (e. g. VGA addresses). It is unlikely, however, that the
* memory controller would generate an error on that range.
*/
- if ((addr > (u64) pvt->tolm) && (addr < (1L << 32))) {
+ if ((addr > (u64) pvt->tolm) && (addr < (1LL << 32))) {
sprintf(msg, "Error at TOLM area, on addr 0x%08Lx", addr);
edac_mc_handle_ce_no_info(mci, msg);
return -EINVAL;
@@ -913,7 +923,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
addr,
limit,
sad_way + 7,
- INTERLEAVE_MODE(reg) ? "" : "XOR[18:16]");
+ interleave_mode ? "" : "XOR[18:16]");
if (interleave_mode)
idx = ((addr >> 6) ^ (addr >> 16)) & 7;
else
@@ -1053,7 +1063,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
ch_addr = addr & 0x7f;
/* Remove socket wayness and remove 6 bits */
addr >>= 6;
- addr /= sck_xch;
+ addr = div_u64(addr, sck_xch);
#if 0
/* Divide by channel way */
addr = addr / ch_way;
@@ -1073,10 +1083,10 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
continue;
limit = RIR_LIMIT(reg);
-
- debugf0("RIR#%d, limit: %Lu.%03Lu GB (0x%016Lx), way: %d\n",
+ mb = div_u64_rem(limit >> 20, 1000, &kb);
+ debugf0("RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d\n",
n_rir,
- (limit >> 20) / 1000, (limit >> 20) % 1000,
+ mb, kb,
limit,
1 << RIR_WAY(reg));
if (ch_addr <= limit)
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 22c6df5..2e6b245 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -44,7 +44,6 @@
#include <linux/wait.h>
#include <linux/workqueue.h>
-#include <asm/system.h>
#include "core.h"
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c
index afa7c83..68109e9 100644
--- a/drivers/firewire/core-device.c
+++ b/drivers/firewire/core-device.c
@@ -40,7 +40,6 @@
#include <linux/atomic.h>
#include <asm/byteorder.h>
-#include <asm/system.h>
#include "core.h"
diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c
index 255646f..0de8350 100644
--- a/drivers/firewire/core-topology.c
+++ b/drivers/firewire/core-topology.c
@@ -31,7 +31,6 @@
#include <linux/atomic.h>
#include <asm/byteorder.h>
-#include <asm/system.h>
#include "core.h"
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 187b3f2..2b54600 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -46,7 +46,6 @@
#include <asm/byteorder.h>
#include <asm/page.h>
-#include <asm/system.h>
#ifdef CONFIG_PPC_PMAC
#include <asm/pmac_feature.h>
diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c
index 000a29f..b7e65d7 100644
--- a/drivers/firewire/sbp2.c
+++ b/drivers/firewire/sbp2.c
@@ -52,7 +52,6 @@
#include <linux/workqueue.h>
#include <asm/byteorder.h>
-#include <asm/system.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 0409cf3..edadbda 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -236,6 +236,12 @@ config GPIO_MAX732X_IRQ
Say yes here to enable the max732x to be used as an interrupt
controller. It requires the driver to be built in the kernel.
+config GPIO_MC9S08DZ60
+ bool "MX35 3DS BOARD MC9S08DZ60 GPIO functions"
+ depends on I2C && MACH_MX35_3DS
+ help
+ Select this to enable the MC9S08DZ60 GPIO driver
+
config GPIO_PCA953X
tristate "PCA953x, PCA955x, TCA64xx, and MAX7310 I/O ports"
depends on I2C
@@ -422,6 +428,14 @@ config GPIO_ML_IOH
Hub) which is for IVI(In-Vehicle Infotainment) use.
This driver can access the IOH's GPIO device.
+config GPIO_SODAVILLE
+ bool "Intel Sodaville GPIO support"
+ depends on X86 && PCI && OF && BROKEN
+ select GPIO_GENERIC
+ select GENERIC_IRQ_CHIP
+ help
+ Say Y here to support Intel Sodaville GPIO.
+
config GPIO_TIMBERDALE
bool "Support for timberdale GPIO IP"
depends on MFD_TIMBERDALE && HAS_IOMEM
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 9a8fb54..007f54b 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o
obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o
obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o
+obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o
obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o
obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o
obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o
@@ -46,6 +47,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
+obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index df0d595..3d00016 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -313,10 +313,16 @@ static int gpio_to_irq_unbanked(struct gpio_chip *chip, unsigned offset)
return -ENODEV;
}
-static int gpio_irq_type_unbanked(struct irq_data *d, unsigned trigger)
+static int gpio_irq_type_unbanked(struct irq_data *data, unsigned trigger)
{
- struct davinci_gpio_regs __iomem *g = irq2regs(d->irq);
- u32 mask = (u32) irq_data_get_irq_handler_data(d);
+ struct davinci_gpio_controller *d;
+ struct davinci_gpio_regs __iomem *g;
+ struct davinci_soc_info *soc_info = &davinci_soc_info;
+ u32 mask;
+
+ d = (struct davinci_gpio_controller *)data->handler_data;
+ g = (struct davinci_gpio_regs __iomem *)d->regs;
+ mask = __gpio_mask(data->irq - soc_info->gpio_irq);
if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
return -EINVAL;
@@ -380,7 +386,7 @@ static int __init davinci_gpio_irq_setup(void)
* IRQ mux conflicts; gpio_irq_type_unbanked() is only for GPIOs.
*/
if (soc_info->gpio_unbanked) {
- static struct irq_chip gpio_irqchip_unbanked;
+ static struct irq_chip_type gpio_unbanked;
/* pass "bank 0" GPIO IRQs to AINTC */
chips[0].chip.to_irq = gpio_to_irq_unbanked;
@@ -388,9 +394,10 @@ static int __init davinci_gpio_irq_setup(void)
/* AINTC handles mask/unmask; GPIO handles triggering */
irq = bank_irq;
- gpio_irqchip_unbanked = *irq_get_chip(irq);
- gpio_irqchip_unbanked.name = "GPIO-AINTC";
- gpio_irqchip_unbanked.irq_set_type = gpio_irq_type_unbanked;
+ gpio_unbanked = *container_of(irq_get_chip(irq),
+ struct irq_chip_type, chip);
+ gpio_unbanked.chip.name = "GPIO-AINTC";
+ gpio_unbanked.chip.irq_set_type = gpio_irq_type_unbanked;
/* default trigger: both edges */
g = gpio2regs(0);
@@ -399,9 +406,8 @@ static int __init davinci_gpio_irq_setup(void)
/* set the direct IRQs up to use that irqchip */
for (gpio = 0; gpio < soc_info->gpio_unbanked; gpio++, irq++) {
- irq_set_chip(irq, &gpio_irqchip_unbanked);
- irq_set_handler_data(irq, (void *)__gpio_mask(gpio));
- irq_set_chip_data(irq, (__force void *)g);
+ irq_set_chip(irq, &gpio_unbanked.chip);
+ irq_set_handler_data(irq, &chips[gpio / 32]);
irq_set_status_flags(irq, IRQ_TYPE_EDGE_BOTH);
}
diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c
index 4ca5642..776b772 100644
--- a/drivers/gpio/gpio-ep93xx.c
+++ b/drivers/gpio/gpio-ep93xx.c
@@ -12,8 +12,6 @@
* published by the Free Software Foundation.
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -65,11 +63,6 @@ static void ep93xx_gpio_update_int_params(unsigned port)
EP93XX_GPIO_REG(int_en_register_offset[port]));
}
-static inline void ep93xx_gpio_int_mask(unsigned line)
-{
- gpio_int_unmasked[line >> 3] &= ~(1 << (line & 7));
-}
-
static void ep93xx_gpio_int_debounce(unsigned int irq, bool enable)
{
int line = irq_to_gpio(irq);
@@ -212,7 +205,6 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type)
handler = handle_edge_irq;
break;
default:
- pr_err("failed to set irq type %d for gpio %d\n", type, gpio);
return -EINVAL;
}
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
index ddfacc5..61c2d08 100644
--- a/drivers/gpio/gpio-lpc32xx.c
+++ b/drivers/gpio/gpio-lpc32xx.c
@@ -59,12 +59,14 @@
#define GPO3_PIN_TO_BIT(x) (1 << (x))
#define GPIO012_PIN_IN_SEL(x, y) (((x) >> (y)) & 1)
#define GPIO3_PIN_IN_SHIFT(x) ((x) == 5 ? 24 : 10 + (x))
-#define GPIO3_PIN_IN_SEL(x, y) ((x) >> GPIO3_PIN_IN_SHIFT(y))
+#define GPIO3_PIN_IN_SEL(x, y) (((x) >> GPIO3_PIN_IN_SHIFT(y)) & 1)
#define GPIO3_PIN5_IN_SEL(x) (((x) >> 24) & 1)
#define GPI3_PIN_IN_SEL(x, y) (((x) >> (y)) & 1)
+#define GPO3_PIN_IN_SEL(x, y) (((x) >> (y)) & 1)
struct gpio_regs {
void __iomem *inp_state;
+ void __iomem *outp_state;
void __iomem *outp_set;
void __iomem *outp_clr;
void __iomem *dir_set;
@@ -145,6 +147,7 @@ static struct gpio_regs gpio_grp_regs_p2 = {
static struct gpio_regs gpio_grp_regs_p3 = {
.inp_state = LPC32XX_GPIO_P3_INP_STATE,
+ .outp_state = LPC32XX_GPIO_P3_OUTP_STATE,
.outp_set = LPC32XX_GPIO_P3_OUTP_SET,
.outp_clr = LPC32XX_GPIO_P3_OUTP_CLR,
.dir_set = LPC32XX_GPIO_P2_DIR_SET,
@@ -240,6 +243,12 @@ static int __get_gpi_state_p3(struct lpc32xx_gpio_chip *group,
return GPI3_PIN_IN_SEL(__raw_readl(group->gpio_grp->inp_state), pin);
}
+static int __get_gpo_state_p3(struct lpc32xx_gpio_chip *group,
+ unsigned pin)
+{
+ return GPO3_PIN_IN_SEL(__raw_readl(group->gpio_grp->outp_state), pin);
+}
+
/*
* GENERIC_GPIO primitives.
*/
@@ -340,6 +349,13 @@ static void lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned pin,
__set_gpo_level_p3(group, pin, value);
}
+static int lpc32xx_gpo_get_value(struct gpio_chip *chip, unsigned pin)
+{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ return __get_gpo_state_p3(group, pin);
+}
+
static int lpc32xx_gpio_request(struct gpio_chip *chip, unsigned pin)
{
if (pin < chip->ngpio)
@@ -427,6 +443,7 @@ static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = {
.label = "gpo_p3",
.direction_output = lpc32xx_gpio_dir_out_always,
.set = lpc32xx_gpo_set_value,
+ .get = lpc32xx_gpo_get_value,
.request = lpc32xx_gpio_request,
.base = LPC32XX_GPO_P3_GRP,
.ngpio = LPC32XX_GPO_P3_MAX,
diff --git a/drivers/gpio/gpio-mc9s08dz60.c b/drivers/gpio/gpio-mc9s08dz60.c
new file mode 100644
index 0000000..2738cc4
--- /dev/null
+++ b/drivers/gpio/gpio-mc9s08dz60.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2009-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Author: Wu Guoxing <b39297@freescale.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+
+#define GPIO_GROUP_NUM 2
+#define GPIO_NUM_PER_GROUP 8
+#define GPIO_NUM (GPIO_GROUP_NUM*GPIO_NUM_PER_GROUP)
+
+struct mc9s08dz60 {
+ struct i2c_client *client;
+ struct gpio_chip chip;
+};
+
+static inline struct mc9s08dz60 *to_mc9s08dz60(struct gpio_chip *gc)
+{
+ return container_of(gc, struct mc9s08dz60, chip);
+}
+
+
+static void mc9s_gpio_to_reg_and_bit(int offset, u8 *reg, u8 *bit)
+{
+ *reg = 0x20 + offset / GPIO_NUM_PER_GROUP;
+ *bit = offset % GPIO_NUM_PER_GROUP;
+}
+
+static int mc9s08dz60_get_value(struct gpio_chip *gc, unsigned offset)
+{
+ u8 reg, bit;
+ s32 value;
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ mc9s_gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+
+ return (value >= 0) ? (value >> bit) & 0x1 : 0;
+}
+
+static int mc9s08dz60_set(struct mc9s08dz60 *mc9s, unsigned offset, int val)
+{
+ u8 reg, bit;
+ s32 value;
+
+ mc9s_gpio_to_reg_and_bit(offset, &reg, &bit);
+ value = i2c_smbus_read_byte_data(mc9s->client, reg);
+ if (value >= 0) {
+ if (val)
+ value |= 1 << bit;
+ else
+ value &= ~(1 << bit);
+
+ return i2c_smbus_write_byte_data(mc9s->client, reg, value);
+ } else
+ return value;
+
+}
+
+
+static void mc9s08dz60_set_value(struct gpio_chip *gc, unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_direction_output(struct gpio_chip *gc,
+ unsigned offset, int val)
+{
+ struct mc9s08dz60 *mc9s = to_mc9s08dz60(gc);
+
+ return mc9s08dz60_set(mc9s, offset, val);
+}
+
+static int mc9s08dz60_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct mc9s08dz60 *mc9s;
+
+ mc9s = kzalloc(sizeof(*mc9s), GFP_KERNEL);
+ if (!mc9s)
+ return -ENOMEM;
+
+ mc9s->chip.label = client->name;
+ mc9s->chip.base = -1;
+ mc9s->chip.dev = &client->dev;
+ mc9s->chip.owner = THIS_MODULE;
+ mc9s->chip.ngpio = GPIO_NUM;
+ mc9s->chip.can_sleep = 1;
+ mc9s->chip.get = mc9s08dz60_get_value;
+ mc9s->chip.set = mc9s08dz60_set_value;
+ mc9s->chip.direction_output = mc9s08dz60_direction_output;
+ mc9s->client = client;
+ i2c_set_clientdata(client, mc9s);
+
+ ret = gpiochip_add(&mc9s->chip);
+ if (ret)
+ goto error;
+
+ return 0;
+
+ error:
+ kfree(mc9s);
+ return ret;
+}
+
+static int mc9s08dz60_remove(struct i2c_client *client)
+{
+ struct mc9s08dz60 *mc9s;
+ int ret;
+
+ mc9s = i2c_get_clientdata(client);
+
+ ret = gpiochip_remove(&mc9s->chip);
+ if (!ret)
+ kfree(mc9s);
+
+ return ret;
+
+}
+
+static const struct i2c_device_id mc9s08dz60_id[] = {
+ {"mc9s08dz60", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mc9s08dz60_id);
+
+static struct i2c_driver mc9s08dz60_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mc9s08dz60",
+ },
+ .probe = mc9s08dz60_probe,
+ .remove = mc9s08dz60_remove,
+ .id_table = mc9s08dz60_id,
+};
+
+module_i2c_driver(mc9s08dz60_i2c_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc. "
+ "Wu Guoxing <b39297@freescale.com>");
+MODULE_DESCRIPTION("mc9s08dz60 gpio function on mx35 3ds board");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index f49bd6f..1adc2ec 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -19,9 +19,12 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
-#include <linux/slab.h>
+#include <linux/device.h>
#include <linux/pm_runtime.h>
#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/irqdomain.h>
#include <mach/hardware.h>
#include <asm/irq.h>
@@ -50,10 +53,10 @@ struct gpio_regs {
struct gpio_bank {
struct list_head node;
- unsigned long pbase;
void __iomem *base;
u16 irq;
- u16 virtual_irq_start;
+ int irq_base;
+ struct irq_domain *domain;
u32 suspend_wakeup;
u32 saved_wakeup;
u32 non_wakeup_gpios;
@@ -77,7 +80,6 @@ struct gpio_bank {
int stride;
u32 width;
int context_loss_count;
- u16 id;
int power_mode;
bool workaround_enabled;
@@ -91,6 +93,11 @@ struct gpio_bank {
#define GPIO_BIT(bank, gpio) (1 << GPIO_INDEX(bank, gpio))
#define GPIO_MOD_CTRL_BIT BIT(0)
+static int irq_to_gpio(struct gpio_bank *bank, unsigned int gpio_irq)
+{
+ return gpio_irq - bank->irq_base + bank->chip.base;
+}
+
static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
{
void __iomem *reg = bank->base;
@@ -113,10 +120,13 @@ static void _set_gpio_dataout_reg(struct gpio_bank *bank, int gpio, int enable)
void __iomem *reg = bank->base;
u32 l = GPIO_BIT(bank, gpio);
- if (enable)
+ if (enable) {
reg += bank->regs->set_dataout;
- else
+ bank->context.dataout |= l;
+ } else {
reg += bank->regs->clr_dataout;
+ bank->context.dataout &= ~l;
+ }
__raw_writel(l, reg);
}
@@ -137,25 +147,25 @@ static void _set_gpio_dataout_mask(struct gpio_bank *bank, int gpio, int enable)
bank->context.dataout = l;
}
-static int _get_gpio_datain(struct gpio_bank *bank, int gpio)
+static int _get_gpio_datain(struct gpio_bank *bank, int offset)
{
void __iomem *reg = bank->base + bank->regs->datain;
- return (__raw_readl(reg) & GPIO_BIT(bank, gpio)) != 0;
+ return (__raw_readl(reg) & (1 << offset)) != 0;
}
-static int _get_gpio_dataout(struct gpio_bank *bank, int gpio)
+static int _get_gpio_dataout(struct gpio_bank *bank, int offset)
{
void __iomem *reg = bank->base + bank->regs->dataout;
- return (__raw_readl(reg) & GPIO_BIT(bank, gpio)) != 0;
+ return (__raw_readl(reg) & (1 << offset)) != 0;
}
static inline void _gpio_rmw(void __iomem *base, u32 reg, u32 mask, bool set)
{
int l = __raw_readl(base + reg);
- if (set)
+ if (set)
l |= mask;
else
l &= ~mask;
@@ -238,7 +248,7 @@ static void _set_gpio_debounce(struct gpio_bank *bank, unsigned gpio,
}
static inline void set_gpio_trigger(struct gpio_bank *bank, int gpio,
- int trigger)
+ unsigned trigger)
{
void __iomem *base = bank->base;
u32 gpio_bit = 1 << gpio;
@@ -320,7 +330,8 @@ static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio)
static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio) {}
#endif
-static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger)
+static int _set_gpio_triggering(struct gpio_bank *bank, int gpio,
+ unsigned trigger)
{
void __iomem *reg = bank->base;
void __iomem *base = bank->base;
@@ -367,7 +378,7 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger)
static int gpio_irq_type(struct irq_data *d, unsigned type)
{
- struct gpio_bank *bank;
+ struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
unsigned gpio;
int retval;
unsigned long flags;
@@ -375,13 +386,11 @@ static int gpio_irq_type(struct irq_data *d, unsigned type)
if (!cpu_class_is_omap2() && d->irq > IH_MPUIO_BASE)
gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE);
else
- gpio = d->irq - IH_GPIO_BASE;
+ gpio = irq_to_gpio(bank, d->irq);
if (type & ~IRQ_TYPE_SENSE_MASK)
return -EINVAL;
- bank = irq_data_get_irq_chip_data(d);
-
if (!bank->regs->leveldetect0 &&
(type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
return -EINVAL;
@@ -442,6 +451,7 @@ static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
if (bank->regs->set_irqenable) {
reg += bank->regs->set_irqenable;
l = gpio_mask;
+ bank->context.irqenable1 |= gpio_mask;
} else {
reg += bank->regs->irqenable;
l = __raw_readl(reg);
@@ -449,10 +459,10 @@ static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
l &= ~gpio_mask;
else
l |= gpio_mask;
+ bank->context.irqenable1 = l;
}
__raw_writel(l, reg);
- bank->context.irqenable1 = l;
}
static void _disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
@@ -463,6 +473,7 @@ static void _disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
if (bank->regs->clr_irqenable) {
reg += bank->regs->clr_irqenable;
l = gpio_mask;
+ bank->context.irqenable1 &= ~gpio_mask;
} else {
reg += bank->regs->irqenable;
l = __raw_readl(reg);
@@ -470,15 +481,18 @@ static void _disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
l |= gpio_mask;
else
l &= ~gpio_mask;
+ bank->context.irqenable1 = l;
}
__raw_writel(l, reg);
- bank->context.irqenable1 = l;
}
static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable)
{
- _enable_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
+ if (enable)
+ _enable_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
+ else
+ _disable_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
}
/*
@@ -495,7 +509,7 @@ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
unsigned long flags;
if (bank->non_wakeup_gpios & gpio_bit) {
- dev_err(bank->dev,
+ dev_err(bank->dev,
"Unable to modify wakeup on non-wakeup GPIO%d\n", gpio);
return -EINVAL;
}
@@ -506,6 +520,7 @@ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
else
bank->suspend_wakeup &= ~gpio_bit;
+ __raw_writel(bank->suspend_wakeup, bank->base + bank->regs->wkup_en);
spin_unlock_irqrestore(&bank->lock, flags);
return 0;
@@ -522,14 +537,10 @@ static void _reset_gpio(struct gpio_bank *bank, int gpio)
/* Use disable_irq_wake() and enable_irq_wake() functions from drivers */
static int gpio_wake_enable(struct irq_data *d, unsigned int enable)
{
- unsigned int gpio = d->irq - IH_GPIO_BASE;
- struct gpio_bank *bank;
- int retval;
-
- bank = irq_data_get_irq_chip_data(d);
- retval = _set_gpio_wakeup(bank, gpio, enable);
+ struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ unsigned int gpio = irq_to_gpio(bank, d->irq);
- return retval;
+ return _set_gpio_wakeup(bank, gpio, enable);
}
static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
@@ -671,13 +682,15 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
if (!isr)
break;
- gpio_irq = bank->virtual_irq_start;
+ gpio_irq = bank->irq_base;
for (; isr != 0; isr >>= 1, gpio_irq++) {
- gpio_index = GPIO_INDEX(bank, irq_to_gpio(gpio_irq));
+ int gpio = irq_to_gpio(bank, gpio_irq);
if (!(isr & 1))
continue;
+ gpio_index = GPIO_INDEX(bank, gpio);
+
/*
* Some chips can't respond to both rising and falling
* at the same time. If this irq was requested with
@@ -703,8 +716,8 @@ exit:
static void gpio_irq_shutdown(struct irq_data *d)
{
- unsigned int gpio = d->irq - IH_GPIO_BASE;
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ unsigned int gpio = irq_to_gpio(bank, d->irq);
unsigned long flags;
spin_lock_irqsave(&bank->lock, flags);
@@ -714,16 +727,16 @@ static void gpio_irq_shutdown(struct irq_data *d)
static void gpio_ack_irq(struct irq_data *d)
{
- unsigned int gpio = d->irq - IH_GPIO_BASE;
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ unsigned int gpio = irq_to_gpio(bank, d->irq);
_clear_gpio_irqstatus(bank, gpio);
}
static void gpio_mask_irq(struct irq_data *d)
{
- unsigned int gpio = d->irq - IH_GPIO_BASE;
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ unsigned int gpio = irq_to_gpio(bank, d->irq);
unsigned long flags;
spin_lock_irqsave(&bank->lock, flags);
@@ -734,8 +747,8 @@ static void gpio_mask_irq(struct irq_data *d)
static void gpio_unmask_irq(struct irq_data *d)
{
- unsigned int gpio = d->irq - IH_GPIO_BASE;
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ unsigned int gpio = irq_to_gpio(bank, d->irq);
unsigned int irq_mask = GPIO_BIT(bank, gpio);
u32 trigger = irqd_get_trigger_type(d);
unsigned long flags;
@@ -852,19 +865,15 @@ static int gpio_is_input(struct gpio_bank *bank, int mask)
static int gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank;
- void __iomem *reg;
- int gpio;
u32 mask;
- gpio = chip->base + offset;
bank = container_of(chip, struct gpio_bank, chip);
- reg = bank->base;
- mask = GPIO_BIT(bank, gpio);
+ mask = (1 << offset);
if (gpio_is_input(bank, mask))
- return _get_gpio_datain(bank, gpio);
+ return _get_gpio_datain(bank, offset);
else
- return _get_gpio_dataout(bank, gpio);
+ return _get_gpio_dataout(bank, offset);
}
static int gpio_output(struct gpio_chip *chip, unsigned offset, int value)
@@ -917,7 +926,7 @@ static int gpio_2irq(struct gpio_chip *chip, unsigned offset)
struct gpio_bank *bank;
bank = container_of(chip, struct gpio_bank, chip);
- return bank->virtual_irq_start + offset;
+ return bank->irq_base + offset;
}
/*---------------------------------------------------------------------*/
@@ -970,7 +979,7 @@ static void omap_gpio_mod_init(struct gpio_bank *bank)
_gpio_rmw(base, bank->regs->ctrl, 0, 1);
}
-static __init void
+static __devinit void
omap_mpuio_alloc_gc(struct gpio_bank *bank, unsigned int irq_start,
unsigned int num)
{
@@ -1030,8 +1039,7 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank)
gpiochip_add(&bank->chip);
- for (j = bank->virtual_irq_start;
- j < bank->virtual_irq_start + bank->width; j++) {
+ for (j = bank->irq_base; j < bank->irq_base + bank->width; j++) {
irq_set_lockdep_class(j, &gpio_lock_class);
irq_set_chip_data(j, bank);
if (bank->is_mpuio) {
@@ -1046,39 +1054,38 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank)
irq_set_handler_data(bank->irq, bank);
}
+static const struct of_device_id omap_gpio_match[];
+
static int __devinit omap_gpio_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ const struct of_device_id *match;
struct omap_gpio_platform_data *pdata;
struct resource *res;
struct gpio_bank *bank;
int ret = 0;
- if (!pdev->dev.platform_data) {
- ret = -EINVAL;
- goto err_exit;
- }
+ match = of_match_device(of_match_ptr(omap_gpio_match), dev);
+
+ pdata = match ? match->data : dev->platform_data;
+ if (!pdata)
+ return -EINVAL;
- bank = kzalloc(sizeof(struct gpio_bank), GFP_KERNEL);
+ bank = devm_kzalloc(&pdev->dev, sizeof(struct gpio_bank), GFP_KERNEL);
if (!bank) {
- dev_err(&pdev->dev, "Memory alloc failed for gpio_bank\n");
- ret = -ENOMEM;
- goto err_exit;
+ dev_err(dev, "Memory alloc failed\n");
+ return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (unlikely(!res)) {
- dev_err(&pdev->dev, "GPIO Bank %i Invalid IRQ resource\n",
- pdev->id);
- ret = -ENODEV;
- goto err_free;
+ dev_err(dev, "Invalid IRQ resource\n");
+ return -ENODEV;
}
bank->irq = res->start;
- bank->id = pdev->id;
-
- pdata = pdev->dev.platform_data;
- bank->virtual_irq_start = pdata->virtual_irq_start;
- bank->dev = &pdev->dev;
+ bank->dev = dev;
bank->dbck_flag = pdata->dbck_flag;
bank->stride = pdata->bank_stride;
bank->width = pdata->bank_width;
@@ -1087,6 +1094,18 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev)
bank->loses_context = pdata->loses_context;
bank->get_context_loss_count = pdata->get_context_loss_count;
bank->regs = pdata->regs;
+#ifdef CONFIG_OF_GPIO
+ bank->chip.of_node = of_node_get(node);
+#endif
+
+ bank->irq_base = irq_alloc_descs(-1, 0, bank->width, 0);
+ if (bank->irq_base < 0) {
+ dev_err(dev, "Couldn't allocate IRQ numbers\n");
+ return -ENODEV;
+ }
+
+ bank->domain = irq_domain_add_legacy(node, bank->width, bank->irq_base,
+ 0, &irq_domain_simple_ops, NULL);
if (bank->regs->set_dataout && bank->regs->clr_dataout)
bank->set_dataout = _set_gpio_dataout_reg;
@@ -1098,18 +1117,20 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev)
/* Static mapping, never released */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!res)) {
- dev_err(&pdev->dev, "GPIO Bank %i Invalid mem resource\n",
- pdev->id);
- ret = -ENODEV;
- goto err_free;
+ dev_err(dev, "Invalid mem resource\n");
+ return -ENODEV;
}
- bank->base = ioremap(res->start, resource_size(res));
+ if (!devm_request_mem_region(dev, res->start, resource_size(res),
+ pdev->name)) {
+ dev_err(dev, "Region already claimed\n");
+ return -EBUSY;
+ }
+
+ bank->base = devm_ioremap(dev, res->start, resource_size(res));
if (!bank->base) {
- dev_err(&pdev->dev, "Could not ioremap gpio bank%i\n",
- pdev->id);
- ret = -ENOMEM;
- goto err_free;
+ dev_err(dev, "Could not ioremap\n");
+ return -ENOMEM;
}
platform_set_drvdata(pdev, bank);
@@ -1130,11 +1151,6 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev)
list_add_tail(&bank->node, &omap_gpio_list);
return ret;
-
-err_free:
- kfree(bank);
-err_exit:
- return ret;
}
#ifdef CONFIG_ARCH_OMAP2PLUS
@@ -1196,8 +1212,30 @@ static int omap_gpio_runtime_suspend(struct device *dev)
struct gpio_bank *bank = platform_get_drvdata(pdev);
u32 l1 = 0, l2 = 0;
unsigned long flags;
+ u32 wake_low, wake_hi;
spin_lock_irqsave(&bank->lock, flags);
+
+ /*
+ * Only edges can generate a wakeup event to the PRCM.
+ *
+ * Therefore, ensure any wake-up capable GPIOs have
+ * edge-detection enabled before going idle to ensure a wakeup
+ * to the PRCM is generated on a GPIO transition. (c.f. 34xx
+ * NDA TRM 25.5.3.1)
+ *
+ * The normal values will be restored upon ->runtime_resume()
+ * by writing back the values saved in bank->context.
+ */
+ wake_low = bank->context.leveldetect0 & bank->context.wake_en;
+ if (wake_low)
+ __raw_writel(wake_low | bank->context.fallingdetect,
+ bank->base + bank->regs->fallingdetect);
+ wake_hi = bank->context.leveldetect1 & bank->context.wake_en;
+ if (wake_hi)
+ __raw_writel(wake_hi | bank->context.risingdetect,
+ bank->base + bank->regs->risingdetect);
+
if (bank->power_mode != OFF_MODE) {
bank->power_mode = 0;
goto update_gpio_context_count;
@@ -1207,9 +1245,6 @@ static int omap_gpio_runtime_suspend(struct device *dev)
* non-wakeup GPIOs. Otherwise spurious IRQs will be
* generated. See OMAP2420 Errata item 1.101.
*/
- if (!(bank->enabled_non_wakeup_gpios))
- goto update_gpio_context_count;
-
bank->saved_datain = __raw_readl(bank->base +
bank->regs->datain);
l1 = __raw_readl(bank->base + bank->regs->fallingdetect);
@@ -1246,7 +1281,19 @@ static int omap_gpio_runtime_resume(struct device *dev)
spin_lock_irqsave(&bank->lock, flags);
_gpio_dbck_enable(bank);
- if (!bank->enabled_non_wakeup_gpios || !bank->workaround_enabled) {
+
+ /*
+ * In ->runtime_suspend(), level-triggered, wakeup-enabled
+ * GPIOs were set to edge trigger also in order to be able to
+ * generate a PRCM wakeup. Here we restore the
+ * pre-runtime_suspend() values for edge triggering.
+ */
+ __raw_writel(bank->context.fallingdetect,
+ bank->base + bank->regs->fallingdetect);
+ __raw_writel(bank->context.risingdetect,
+ bank->base + bank->regs->risingdetect);
+
+ if (!bank->workaround_enabled) {
spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
@@ -1397,11 +1444,95 @@ static const struct dev_pm_ops gpio_pm_ops = {
NULL)
};
+#if defined(CONFIG_OF)
+static struct omap_gpio_reg_offs omap2_gpio_regs = {
+ .revision = OMAP24XX_GPIO_REVISION,
+ .direction = OMAP24XX_GPIO_OE,
+ .datain = OMAP24XX_GPIO_DATAIN,
+ .dataout = OMAP24XX_GPIO_DATAOUT,
+ .set_dataout = OMAP24XX_GPIO_SETDATAOUT,
+ .clr_dataout = OMAP24XX_GPIO_CLEARDATAOUT,
+ .irqstatus = OMAP24XX_GPIO_IRQSTATUS1,
+ .irqstatus2 = OMAP24XX_GPIO_IRQSTATUS2,
+ .irqenable = OMAP24XX_GPIO_IRQENABLE1,
+ .irqenable2 = OMAP24XX_GPIO_IRQENABLE2,
+ .set_irqenable = OMAP24XX_GPIO_SETIRQENABLE1,
+ .clr_irqenable = OMAP24XX_GPIO_CLEARIRQENABLE1,
+ .debounce = OMAP24XX_GPIO_DEBOUNCE_VAL,
+ .debounce_en = OMAP24XX_GPIO_DEBOUNCE_EN,
+ .ctrl = OMAP24XX_GPIO_CTRL,
+ .wkup_en = OMAP24XX_GPIO_WAKE_EN,
+ .leveldetect0 = OMAP24XX_GPIO_LEVELDETECT0,
+ .leveldetect1 = OMAP24XX_GPIO_LEVELDETECT1,
+ .risingdetect = OMAP24XX_GPIO_RISINGDETECT,
+ .fallingdetect = OMAP24XX_GPIO_FALLINGDETECT,
+};
+
+static struct omap_gpio_reg_offs omap4_gpio_regs = {
+ .revision = OMAP4_GPIO_REVISION,
+ .direction = OMAP4_GPIO_OE,
+ .datain = OMAP4_GPIO_DATAIN,
+ .dataout = OMAP4_GPIO_DATAOUT,
+ .set_dataout = OMAP4_GPIO_SETDATAOUT,
+ .clr_dataout = OMAP4_GPIO_CLEARDATAOUT,
+ .irqstatus = OMAP4_GPIO_IRQSTATUS0,
+ .irqstatus2 = OMAP4_GPIO_IRQSTATUS1,
+ .irqenable = OMAP4_GPIO_IRQSTATUSSET0,
+ .irqenable2 = OMAP4_GPIO_IRQSTATUSSET1,
+ .set_irqenable = OMAP4_GPIO_IRQSTATUSSET0,
+ .clr_irqenable = OMAP4_GPIO_IRQSTATUSCLR0,
+ .debounce = OMAP4_GPIO_DEBOUNCINGTIME,
+ .debounce_en = OMAP4_GPIO_DEBOUNCENABLE,
+ .ctrl = OMAP4_GPIO_CTRL,
+ .wkup_en = OMAP4_GPIO_IRQWAKEN0,
+ .leveldetect0 = OMAP4_GPIO_LEVELDETECT0,
+ .leveldetect1 = OMAP4_GPIO_LEVELDETECT1,
+ .risingdetect = OMAP4_GPIO_RISINGDETECT,
+ .fallingdetect = OMAP4_GPIO_FALLINGDETECT,
+};
+
+static struct omap_gpio_platform_data omap2_pdata = {
+ .regs = &omap2_gpio_regs,
+ .bank_width = 32,
+ .dbck_flag = false,
+};
+
+static struct omap_gpio_platform_data omap3_pdata = {
+ .regs = &omap2_gpio_regs,
+ .bank_width = 32,
+ .dbck_flag = true,
+};
+
+static struct omap_gpio_platform_data omap4_pdata = {
+ .regs = &omap4_gpio_regs,
+ .bank_width = 32,
+ .dbck_flag = true,
+};
+
+static const struct of_device_id omap_gpio_match[] = {
+ {
+ .compatible = "ti,omap4-gpio",
+ .data = &omap4_pdata,
+ },
+ {
+ .compatible = "ti,omap3-gpio",
+ .data = &omap3_pdata,
+ },
+ {
+ .compatible = "ti,omap2-gpio",
+ .data = &omap2_pdata,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, omap_gpio_match);
+#endif
+
static struct platform_driver omap_gpio_driver = {
.probe = omap_gpio_probe,
.driver = {
.name = "omap_gpio",
.pm = &gpio_pm_ops,
+ .of_match_table = of_match_ptr(omap_gpio_match),
},
};
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 77c9cc7..b4b5da4 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -352,7 +352,12 @@ static int pl061_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(pl061_dev_pm_ops, pl061_suspend, pl061_resume);
+static const struct dev_pm_ops pl061_dev_pm_ops = {
+ .suspend = pl061_suspend,
+ .resume = pl061_resume,
+ .freeze = pl061_suspend,
+ .restore = pl061_resume,
+};
#endif
static struct amba_id pl061_ids[] = {
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
index b2d3ee1..5689ce6 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -22,6 +22,8 @@
#include <linux/syscore_ops.h>
#include <linux/slab.h>
+#include <mach/irqs.h>
+
/*
* We handle the GPIOs by banks, each bank covers up to 32 GPIOs with
* one set of registers. The register offsets are organized below:
diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c
index 7eecf69..8ea3b33 100644
--- a/drivers/gpio/gpio-sa1100.c
+++ b/drivers/gpio/gpio-sa1100.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <mach/hardware.h>
+#include <mach/irqs.h>
static int sa1100_gpio_get(struct gpio_chip *chip, unsigned offset)
{
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
index 0a79a11..4627787 100644
--- a/drivers/gpio/gpio-samsung.c
+++ b/drivers/gpio/gpio-samsung.c
@@ -169,7 +169,7 @@ int s3c24xx_gpio_setpull_1down(struct samsung_gpio_chip *chip,
return s3c24xx_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_DOWN);
}
-static int exynos4_gpio_setpull(struct samsung_gpio_chip *chip,
+static int exynos_gpio_setpull(struct samsung_gpio_chip *chip,
unsigned int off, samsung_gpio_pull_t pull)
{
if (pull == S3C_GPIO_PULL_UP)
@@ -178,7 +178,7 @@ static int exynos4_gpio_setpull(struct samsung_gpio_chip *chip,
return samsung_gpio_setpull_updown(chip, off, pull);
}
-static samsung_gpio_pull_t exynos4_gpio_getpull(struct samsung_gpio_chip *chip,
+static samsung_gpio_pull_t exynos_gpio_getpull(struct samsung_gpio_chip *chip,
unsigned int off)
{
samsung_gpio_pull_t pull;
@@ -452,9 +452,9 @@ static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = {
};
#endif
-static struct samsung_gpio_cfg exynos4_gpio_cfg = {
- .set_pull = exynos4_gpio_setpull,
- .get_pull = exynos4_gpio_getpull,
+static struct samsung_gpio_cfg exynos_gpio_cfg = {
+ .set_pull = exynos_gpio_setpull,
+ .get_pull = exynos_gpio_getpull,
.set_config = samsung_gpio_setcfg_4bit,
.get_config = samsung_gpio_getcfg_4bit,
};
@@ -502,13 +502,13 @@ static struct samsung_gpio_cfg samsung_gpio_cfgs[] = {
.get_config = samsung_gpio_getcfg_2bit,
},
[8] = {
- .set_pull = exynos4_gpio_setpull,
- .get_pull = exynos4_gpio_getpull,
+ .set_pull = exynos_gpio_setpull,
+ .get_pull = exynos_gpio_getpull,
},
[9] = {
.cfg_eint = 0x3,
- .set_pull = exynos4_gpio_setpull,
- .get_pull = exynos4_gpio_getpull,
+ .set_pull = exynos_gpio_setpull,
+ .get_pull = exynos_gpio_getpull,
}
};
@@ -2113,10 +2113,10 @@ static struct samsung_gpio_chip s5pv210_gpios_4bit[] = {
};
/*
- * Followings are the gpio banks in EXYNOS4210
+ * Followings are the gpio banks in EXYNOS SoCs
*
* The 'config' member when left to NULL, is initialized to the default
- * structure samsung_gpio_cfgs[3] in the init function below.
+ * structure exynos_gpio_cfg in the init function below.
*
* The 'base' member is also initialized in the init function below.
* Note: The initialization of 'base' member of samsung_gpio_chip structure
@@ -2331,7 +2331,6 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = {
.label = "GPY6",
},
}, {
- .base = (S5P_VA_GPIO2 + 0xC00),
.config = &samsung_gpio_cfgs[9],
.irq_base = IRQ_EINT(0),
.chip = {
@@ -2341,7 +2340,6 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = {
.to_irq = samsung_gpiolib_to_irq,
},
}, {
- .base = (S5P_VA_GPIO2 + 0xC20),
.config = &samsung_gpio_cfgs[9],
.irq_base = IRQ_EINT(8),
.chip = {
@@ -2351,7 +2349,6 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = {
.to_irq = samsung_gpiolib_to_irq,
},
}, {
- .base = (S5P_VA_GPIO2 + 0xC40),
.config = &samsung_gpio_cfgs[9],
.irq_base = IRQ_EINT(16),
.chip = {
@@ -2361,7 +2358,6 @@ static struct samsung_gpio_chip exynos4_gpios_2[] = {
.to_irq = samsung_gpiolib_to_irq,
},
}, {
- .base = (S5P_VA_GPIO2 + 0xC60),
.config = &samsung_gpio_cfgs[9],
.irq_base = IRQ_EINT(24),
.chip = {
@@ -2386,8 +2382,280 @@ static struct samsung_gpio_chip exynos4_gpios_3[] = {
#endif
};
-#if defined(CONFIG_ARCH_EXYNOS4) && defined(CONFIG_OF)
-static int exynos4_gpio_xlate(struct gpio_chip *gc,
+static struct samsung_gpio_chip exynos5_gpios_1[] = {
+#ifdef CONFIG_ARCH_EXYNOS5
+ {
+ .chip = {
+ .base = EXYNOS5_GPA0(0),
+ .ngpio = EXYNOS5_GPIO_A0_NR,
+ .label = "GPA0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPA1(0),
+ .ngpio = EXYNOS5_GPIO_A1_NR,
+ .label = "GPA1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPA2(0),
+ .ngpio = EXYNOS5_GPIO_A2_NR,
+ .label = "GPA2",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPB0(0),
+ .ngpio = EXYNOS5_GPIO_B0_NR,
+ .label = "GPB0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPB1(0),
+ .ngpio = EXYNOS5_GPIO_B1_NR,
+ .label = "GPB1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPB2(0),
+ .ngpio = EXYNOS5_GPIO_B2_NR,
+ .label = "GPB2",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPB3(0),
+ .ngpio = EXYNOS5_GPIO_B3_NR,
+ .label = "GPB3",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPC0(0),
+ .ngpio = EXYNOS5_GPIO_C0_NR,
+ .label = "GPC0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPC1(0),
+ .ngpio = EXYNOS5_GPIO_C1_NR,
+ .label = "GPC1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPC2(0),
+ .ngpio = EXYNOS5_GPIO_C2_NR,
+ .label = "GPC2",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPC3(0),
+ .ngpio = EXYNOS5_GPIO_C3_NR,
+ .label = "GPC3",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPD0(0),
+ .ngpio = EXYNOS5_GPIO_D0_NR,
+ .label = "GPD0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPD1(0),
+ .ngpio = EXYNOS5_GPIO_D1_NR,
+ .label = "GPD1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPY0(0),
+ .ngpio = EXYNOS5_GPIO_Y0_NR,
+ .label = "GPY0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPY1(0),
+ .ngpio = EXYNOS5_GPIO_Y1_NR,
+ .label = "GPY1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPY2(0),
+ .ngpio = EXYNOS5_GPIO_Y2_NR,
+ .label = "GPY2",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPY3(0),
+ .ngpio = EXYNOS5_GPIO_Y3_NR,
+ .label = "GPY3",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPY4(0),
+ .ngpio = EXYNOS5_GPIO_Y4_NR,
+ .label = "GPY4",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPY5(0),
+ .ngpio = EXYNOS5_GPIO_Y5_NR,
+ .label = "GPY5",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPY6(0),
+ .ngpio = EXYNOS5_GPIO_Y6_NR,
+ .label = "GPY6",
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[9],
+ .irq_base = IRQ_EINT(0),
+ .chip = {
+ .base = EXYNOS5_GPX0(0),
+ .ngpio = EXYNOS5_GPIO_X0_NR,
+ .label = "GPX0",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[9],
+ .irq_base = IRQ_EINT(8),
+ .chip = {
+ .base = EXYNOS5_GPX1(0),
+ .ngpio = EXYNOS5_GPIO_X1_NR,
+ .label = "GPX1",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[9],
+ .irq_base = IRQ_EINT(16),
+ .chip = {
+ .base = EXYNOS5_GPX2(0),
+ .ngpio = EXYNOS5_GPIO_X2_NR,
+ .label = "GPX2",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ }, {
+ .config = &samsung_gpio_cfgs[9],
+ .irq_base = IRQ_EINT(24),
+ .chip = {
+ .base = EXYNOS5_GPX3(0),
+ .ngpio = EXYNOS5_GPIO_X3_NR,
+ .label = "GPX3",
+ .to_irq = samsung_gpiolib_to_irq,
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip exynos5_gpios_2[] = {
+#ifdef CONFIG_ARCH_EXYNOS5
+ {
+ .chip = {
+ .base = EXYNOS5_GPE0(0),
+ .ngpio = EXYNOS5_GPIO_E0_NR,
+ .label = "GPE0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPE1(0),
+ .ngpio = EXYNOS5_GPIO_E1_NR,
+ .label = "GPE1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPF0(0),
+ .ngpio = EXYNOS5_GPIO_F0_NR,
+ .label = "GPF0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPF1(0),
+ .ngpio = EXYNOS5_GPIO_F1_NR,
+ .label = "GPF1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPG0(0),
+ .ngpio = EXYNOS5_GPIO_G0_NR,
+ .label = "GPG0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPG1(0),
+ .ngpio = EXYNOS5_GPIO_G1_NR,
+ .label = "GPG1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPG2(0),
+ .ngpio = EXYNOS5_GPIO_G2_NR,
+ .label = "GPG2",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPH0(0),
+ .ngpio = EXYNOS5_GPIO_H0_NR,
+ .label = "GPH0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPH1(0),
+ .ngpio = EXYNOS5_GPIO_H1_NR,
+ .label = "GPH1",
+
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip exynos5_gpios_3[] = {
+#ifdef CONFIG_ARCH_EXYNOS5
+ {
+ .chip = {
+ .base = EXYNOS5_GPV0(0),
+ .ngpio = EXYNOS5_GPIO_V0_NR,
+ .label = "GPV0",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPV1(0),
+ .ngpio = EXYNOS5_GPIO_V1_NR,
+ .label = "GPV1",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPV2(0),
+ .ngpio = EXYNOS5_GPIO_V2_NR,
+ .label = "GPV2",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPV3(0),
+ .ngpio = EXYNOS5_GPIO_V3_NR,
+ .label = "GPV3",
+ },
+ }, {
+ .chip = {
+ .base = EXYNOS5_GPV4(0),
+ .ngpio = EXYNOS5_GPIO_V4_NR,
+ .label = "GPV4",
+ },
+ },
+#endif
+};
+
+static struct samsung_gpio_chip exynos5_gpios_4[] = {
+#ifdef CONFIG_ARCH_EXYNOS5
+ {
+ .chip = {
+ .base = EXYNOS5_GPZ(0),
+ .ngpio = EXYNOS5_GPIO_Z_NR,
+ .label = "GPZ",
+ },
+ },
+#endif
+};
+
+
+#if defined(CONFIG_ARCH_EXYNOS) && defined(CONFIG_OF)
+static int exynos_gpio_xlate(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags)
{
unsigned int pin;
@@ -2413,13 +2681,13 @@ static int exynos4_gpio_xlate(struct gpio_chip *gc,
return gpiospec->args[0];
}
-static const struct of_device_id exynos4_gpio_dt_match[] __initdata = {
+static const struct of_device_id exynos_gpio_dt_match[] __initdata = {
{ .compatible = "samsung,exynos4-gpio", },
{}
};
-static __init void exynos4_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip,
- u64 base, u64 offset)
+static __init void exynos_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip,
+ u64 base, u64 offset)
{
struct gpio_chip *gc = &chip->chip;
u64 address;
@@ -2429,28 +2697,29 @@ static __init void exynos4_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip,
address = chip->base ? base + ((u32)chip->base & 0xfff) : base + offset;
gc->of_node = of_find_matching_node_by_address(NULL,
- exynos4_gpio_dt_match, address);
+ exynos_gpio_dt_match, address);
if (!gc->of_node) {
pr_info("gpio: device tree node not found for gpio controller"
" with base address %08llx\n", address);
return;
}
gc->of_gpio_n_cells = 4;
- gc->of_xlate = exynos4_gpio_xlate;
+ gc->of_xlate = exynos_gpio_xlate;
}
-#elif defined(CONFIG_ARCH_EXYNOS4)
-static __init void exynos4_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip,
- u64 base, u64 offset)
+#elif defined(CONFIG_ARCH_EXYNOS)
+static __init void exynos_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip,
+ u64 base, u64 offset)
{
return;
}
-#endif /* defined(CONFIG_ARCH_EXYNOS4) && defined(CONFIG_OF) */
+#endif /* defined(CONFIG_ARCH_EXYNOS) && defined(CONFIG_OF) */
/* TODO: cleanup soc_is_* */
static __init int samsung_gpiolib_init(void)
{
struct samsung_gpio_chip *chip;
int i, nr_chips;
+ void __iomem *gpio_base1, *gpio_base2, *gpio_base3, *gpio_base4;
int group = 0;
samsung_gpiolib_set_cfg(samsung_gpio_cfgs, ARRAY_SIZE(samsung_gpio_cfgs));
@@ -2516,66 +2785,200 @@ static __init int samsung_gpiolib_init(void)
s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR);
#endif
} else if (soc_is_exynos4210()) {
- group = 0;
+#ifdef CONFIG_CPU_EXYNOS4210
+ void __iomem *gpx_base;
/* gpio part1 */
+ gpio_base1 = ioremap(EXYNOS4_PA_GPIO1, SZ_4K);
+ if (gpio_base1 == NULL) {
+ pr_err("unable to ioremap for gpio_base1\n");
+ goto err_ioremap1;
+ }
+
chip = exynos4_gpios_1;
nr_chips = ARRAY_SIZE(exynos4_gpios_1);
for (i = 0; i < nr_chips; i++, chip++) {
if (!chip->config) {
- chip->config = &exynos4_gpio_cfg;
+ chip->config = &exynos_gpio_cfg;
chip->group = group++;
}
-#ifdef CONFIG_CPU_EXYNOS4210
- exynos4_gpiolib_attach_ofnode(chip,
+ exynos_gpiolib_attach_ofnode(chip,
EXYNOS4_PA_GPIO1, i * 0x20);
-#endif
}
- samsung_gpiolib_add_4bit_chips(exynos4_gpios_1, nr_chips, S5P_VA_GPIO1);
+ samsung_gpiolib_add_4bit_chips(exynos4_gpios_1,
+ nr_chips, gpio_base1);
/* gpio part2 */
+ gpio_base2 = ioremap(EXYNOS4_PA_GPIO2, SZ_4K);
+ if (gpio_base2 == NULL) {
+ pr_err("unable to ioremap for gpio_base2\n");
+ goto err_ioremap2;
+ }
+
+ /* need to set base address for gpx */
+ chip = &exynos4_gpios_2[16];
+ gpx_base = gpio_base2 + 0xC00;
+ for (i = 0; i < 4; i++, chip++, gpx_base += 0x20)
+ chip->base = gpx_base;
+
chip = exynos4_gpios_2;
nr_chips = ARRAY_SIZE(exynos4_gpios_2);
for (i = 0; i < nr_chips; i++, chip++) {
if (!chip->config) {
- chip->config = &exynos4_gpio_cfg;
+ chip->config = &exynos_gpio_cfg;
chip->group = group++;
}
-#ifdef CONFIG_CPU_EXYNOS4210
- exynos4_gpiolib_attach_ofnode(chip,
+ exynos_gpiolib_attach_ofnode(chip,
EXYNOS4_PA_GPIO2, i * 0x20);
-#endif
}
- samsung_gpiolib_add_4bit_chips(exynos4_gpios_2, nr_chips, S5P_VA_GPIO2);
+ samsung_gpiolib_add_4bit_chips(exynos4_gpios_2,
+ nr_chips, gpio_base2);
/* gpio part3 */
+ gpio_base3 = ioremap(EXYNOS4_PA_GPIO3, SZ_256);
+ if (gpio_base3 == NULL) {
+ pr_err("unable to ioremap for gpio_base3\n");
+ goto err_ioremap3;
+ }
+
chip = exynos4_gpios_3;
nr_chips = ARRAY_SIZE(exynos4_gpios_3);
for (i = 0; i < nr_chips; i++, chip++) {
if (!chip->config) {
- chip->config = &exynos4_gpio_cfg;
+ chip->config = &exynos_gpio_cfg;
chip->group = group++;
}
-#ifdef CONFIG_CPU_EXYNOS4210
- exynos4_gpiolib_attach_ofnode(chip,
+ exynos_gpiolib_attach_ofnode(chip,
EXYNOS4_PA_GPIO3, i * 0x20);
-#endif
}
- samsung_gpiolib_add_4bit_chips(exynos4_gpios_3, nr_chips, S5P_VA_GPIO3);
+ samsung_gpiolib_add_4bit_chips(exynos4_gpios_3,
+ nr_chips, gpio_base3);
#if defined(CONFIG_CPU_EXYNOS4210) && defined(CONFIG_S5P_GPIO_INT)
s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS);
s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS);
#endif
+
+#endif /* CONFIG_CPU_EXYNOS4210 */
+ } else if (soc_is_exynos5250()) {
+#ifdef CONFIG_SOC_EXYNOS5250
+ void __iomem *gpx_base;
+
+ /* gpio part1 */
+ gpio_base1 = ioremap(EXYNOS5_PA_GPIO1, SZ_4K);
+ if (gpio_base1 == NULL) {
+ pr_err("unable to ioremap for gpio_base1\n");
+ goto err_ioremap1;
+ }
+
+ /* need to set base address for gpx */
+ chip = &exynos5_gpios_1[20];
+ gpx_base = gpio_base1 + 0xC00;
+ for (i = 0; i < 4; i++, chip++, gpx_base += 0x20)
+ chip->base = gpx_base;
+
+ chip = exynos5_gpios_1;
+ nr_chips = ARRAY_SIZE(exynos5_gpios_1);
+
+ for (i = 0; i < nr_chips; i++, chip++) {
+ if (!chip->config) {
+ chip->config = &exynos_gpio_cfg;
+ chip->group = group++;
+ }
+ exynos_gpiolib_attach_ofnode(chip,
+ EXYNOS5_PA_GPIO1, i * 0x20);
+ }
+ samsung_gpiolib_add_4bit_chips(exynos5_gpios_1,
+ nr_chips, gpio_base1);
+
+ /* gpio part2 */
+ gpio_base2 = ioremap(EXYNOS5_PA_GPIO2, SZ_4K);
+ if (gpio_base2 == NULL) {
+ pr_err("unable to ioremap for gpio_base2\n");
+ goto err_ioremap2;
+ }
+
+ chip = exynos5_gpios_2;
+ nr_chips = ARRAY_SIZE(exynos5_gpios_2);
+
+ for (i = 0; i < nr_chips; i++, chip++) {
+ if (!chip->config) {
+ chip->config = &exynos_gpio_cfg;
+ chip->group = group++;
+ }
+ exynos_gpiolib_attach_ofnode(chip,
+ EXYNOS5_PA_GPIO2, i * 0x20);
+ }
+ samsung_gpiolib_add_4bit_chips(exynos5_gpios_2,
+ nr_chips, gpio_base2);
+
+ /* gpio part3 */
+ gpio_base3 = ioremap(EXYNOS5_PA_GPIO3, SZ_4K);
+ if (gpio_base3 == NULL) {
+ pr_err("unable to ioremap for gpio_base3\n");
+ goto err_ioremap3;
+ }
+
+ /* need to set base address for gpv */
+ exynos5_gpios_3[0].base = gpio_base3;
+ exynos5_gpios_3[1].base = gpio_base3 + 0x20;
+ exynos5_gpios_3[2].base = gpio_base3 + 0x60;
+ exynos5_gpios_3[3].base = gpio_base3 + 0x80;
+ exynos5_gpios_3[4].base = gpio_base3 + 0xC0;
+
+ chip = exynos5_gpios_3;
+ nr_chips = ARRAY_SIZE(exynos5_gpios_3);
+
+ for (i = 0; i < nr_chips; i++, chip++) {
+ if (!chip->config) {
+ chip->config = &exynos_gpio_cfg;
+ chip->group = group++;
+ }
+ exynos_gpiolib_attach_ofnode(chip,
+ EXYNOS5_PA_GPIO3, i * 0x20);
+ }
+ samsung_gpiolib_add_4bit_chips(exynos5_gpios_3,
+ nr_chips, gpio_base3);
+
+ /* gpio part4 */
+ gpio_base4 = ioremap(EXYNOS5_PA_GPIO4, SZ_4K);
+ if (gpio_base4 == NULL) {
+ pr_err("unable to ioremap for gpio_base4\n");
+ goto err_ioremap4;
+ }
+
+ chip = exynos5_gpios_4;
+ nr_chips = ARRAY_SIZE(exynos5_gpios_4);
+
+ for (i = 0; i < nr_chips; i++, chip++) {
+ if (!chip->config) {
+ chip->config = &exynos_gpio_cfg;
+ chip->group = group++;
+ }
+ exynos_gpiolib_attach_ofnode(chip,
+ EXYNOS5_PA_GPIO4, i * 0x20);
+ }
+ samsung_gpiolib_add_4bit_chips(exynos5_gpios_4,
+ nr_chips, gpio_base4);
+#endif /* CONFIG_SOC_EXYNOS5250 */
} else {
WARN(1, "Unknown SoC in gpio-samsung, no GPIOs added\n");
return -ENODEV;
}
return 0;
+
+err_ioremap4:
+ iounmap(gpio_base3);
+err_ioremap3:
+ iounmap(gpio_base2);
+err_ioremap2:
+ iounmap(gpio_base1);
+err_ioremap1:
+ return -ENOMEM;
}
core_initcall(samsung_gpiolib_init);
diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c
new file mode 100644
index 0000000..9ba15d3
--- /dev/null
+++ b/drivers/gpio/gpio-sodaville.c
@@ -0,0 +1,302 @@
+/*
+ * GPIO interface for Intel Sodaville SoCs.
+ *
+ * Copyright (c) 2010, 2011 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/basic_mmio_gpio.h>
+
+#define DRV_NAME "sdv_gpio"
+#define SDV_NUM_PUB_GPIOS 12
+#define PCI_DEVICE_ID_SDV_GPIO 0x2e67
+#define GPIO_BAR 0
+
+#define GPOUTR 0x00
+#define GPOER 0x04
+#define GPINR 0x08
+
+#define GPSTR 0x0c
+#define GPIT1R0 0x10
+#define GPIO_INT 0x14
+#define GPIT1R1 0x18
+
+#define GPMUXCTL 0x1c
+
+struct sdv_gpio_chip_data {
+ int irq_base;
+ void __iomem *gpio_pub_base;
+ struct irq_domain id;
+ struct irq_chip_generic *gc;
+ struct bgpio_chip bgpio;
+};
+
+static int sdv_gpio_pub_set_type(struct irq_data *d, unsigned int type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct sdv_gpio_chip_data *sd = gc->private;
+ void __iomem *type_reg;
+ u32 irq_offs = d->irq - sd->irq_base;
+ u32 reg;
+
+ if (irq_offs < 8)
+ type_reg = sd->gpio_pub_base + GPIT1R0;
+ else
+ type_reg = sd->gpio_pub_base + GPIT1R1;
+
+ reg = readl(type_reg);
+
+ switch (type) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ reg &= ~BIT(4 * (irq_offs % 8));
+ break;
+
+ case IRQ_TYPE_LEVEL_LOW:
+ reg |= BIT(4 * (irq_offs % 8));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ writel(reg, type_reg);
+ return 0;
+}
+
+static irqreturn_t sdv_gpio_pub_irq_handler(int irq, void *data)
+{
+ struct sdv_gpio_chip_data *sd = data;
+ u32 irq_stat = readl(sd->gpio_pub_base + GPSTR);
+
+ irq_stat &= readl(sd->gpio_pub_base + GPIO_INT);
+ if (!irq_stat)
+ return IRQ_NONE;
+
+ while (irq_stat) {
+ u32 irq_bit = __fls(irq_stat);
+
+ irq_stat &= ~BIT(irq_bit);
+ generic_handle_irq(sd->irq_base + irq_bit);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int sdv_xlate(struct irq_domain *h, struct device_node *node,
+ const u32 *intspec, u32 intsize, irq_hw_number_t *out_hwirq,
+ u32 *out_type)
+{
+ u32 line, type;
+
+ if (node != h->of_node)
+ return -EINVAL;
+
+ if (intsize < 2)
+ return -EINVAL;
+
+ line = *intspec;
+ *out_hwirq = line;
+
+ intspec++;
+ type = *intspec;
+
+ switch (type) {
+ case IRQ_TYPE_LEVEL_LOW:
+ case IRQ_TYPE_LEVEL_HIGH:
+ *out_type = type;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct irq_domain_ops irq_domain_sdv_ops = {
+ .dt_translate = sdv_xlate,
+};
+
+static __devinit int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd,
+ struct pci_dev *pdev)
+{
+ struct irq_chip_type *ct;
+ int ret;
+
+ sd->irq_base = irq_alloc_descs(-1, 0, SDV_NUM_PUB_GPIOS, -1);
+ if (sd->irq_base < 0)
+ return sd->irq_base;
+
+ /* mask + ACK all interrupt sources */
+ writel(0, sd->gpio_pub_base + GPIO_INT);
+ writel((1 << 11) - 1, sd->gpio_pub_base + GPSTR);
+
+ ret = request_irq(pdev->irq, sdv_gpio_pub_irq_handler, IRQF_SHARED,
+ "sdv_gpio", sd);
+ if (ret)
+ goto out_free_desc;
+
+ sd->id.irq_base = sd->irq_base;
+ sd->id.of_node = of_node_get(pdev->dev.of_node);
+ sd->id.ops = &irq_domain_sdv_ops;
+
+ /*
+ * This gpio irq controller latches level irqs. Testing shows that if
+ * we unmask & ACK the IRQ before the source of the interrupt is gone
+ * then the interrupt is active again.
+ */
+ sd->gc = irq_alloc_generic_chip("sdv-gpio", 1, sd->irq_base,
+ sd->gpio_pub_base, handle_fasteoi_irq);
+ if (!sd->gc) {
+ ret = -ENOMEM;
+ goto out_free_irq;
+ }
+
+ sd->gc->private = sd;
+ ct = sd->gc->chip_types;
+ ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
+ ct->regs.eoi = GPSTR;
+ ct->regs.mask = GPIO_INT;
+ ct->chip.irq_mask = irq_gc_mask_clr_bit;
+ ct->chip.irq_unmask = irq_gc_mask_set_bit;
+ ct->chip.irq_eoi = irq_gc_eoi;
+ ct->chip.irq_set_type = sdv_gpio_pub_set_type;
+
+ irq_setup_generic_chip(sd->gc, IRQ_MSK(SDV_NUM_PUB_GPIOS),
+ IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST,
+ IRQ_LEVEL | IRQ_NOPROBE);
+
+ irq_domain_add(&sd->id);
+ return 0;
+out_free_irq:
+ free_irq(pdev->irq, sd);
+out_free_desc:
+ irq_free_descs(sd->irq_base, SDV_NUM_PUB_GPIOS);
+ return ret;
+}
+
+static int __devinit sdv_gpio_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ struct sdv_gpio_chip_data *sd;
+ unsigned long addr;
+ const void *prop;
+ int len;
+ int ret;
+ u32 mux_val;
+
+ sd = kzalloc(sizeof(struct sdv_gpio_chip_data), GFP_KERNEL);
+ if (!sd)
+ return -ENOMEM;
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "can't enable device.\n");
+ goto done;
+ }
+
+ ret = pci_request_region(pdev, GPIO_BAR, DRV_NAME);
+ if (ret) {
+ dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR);
+ goto disable_pci;
+ }
+
+ addr = pci_resource_start(pdev, GPIO_BAR);
+ if (!addr)
+ goto release_reg;
+ sd->gpio_pub_base = ioremap(addr, pci_resource_len(pdev, GPIO_BAR));
+
+ prop = of_get_property(pdev->dev.of_node, "intel,muxctl", &len);
+ if (prop && len == 4) {
+ mux_val = of_read_number(prop, 1);
+ writel(mux_val, sd->gpio_pub_base + GPMUXCTL);
+ }
+
+ ret = bgpio_init(&sd->bgpio, &pdev->dev, 4,
+ sd->gpio_pub_base + GPINR, sd->gpio_pub_base + GPOUTR,
+ NULL, sd->gpio_pub_base + GPOER, NULL, false);
+ if (ret)
+ goto unmap;
+ sd->bgpio.gc.ngpio = SDV_NUM_PUB_GPIOS;
+
+ ret = gpiochip_add(&sd->bgpio.gc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "gpiochip_add() failed.\n");
+ goto unmap;
+ }
+
+ ret = sdv_register_irqsupport(sd, pdev);
+ if (ret)
+ goto unmap;
+
+ pci_set_drvdata(pdev, sd);
+ dev_info(&pdev->dev, "Sodaville GPIO driver registered.\n");
+ return 0;
+
+unmap:
+ iounmap(sd->gpio_pub_base);
+release_reg:
+ pci_release_region(pdev, GPIO_BAR);
+disable_pci:
+ pci_disable_device(pdev);
+done:
+ kfree(sd);
+ return ret;
+}
+
+static void sdv_gpio_remove(struct pci_dev *pdev)
+{
+ struct sdv_gpio_chip_data *sd = pci_get_drvdata(pdev);
+
+ irq_domain_del(&sd->id);
+ free_irq(pdev->irq, sd);
+ irq_free_descs(sd->irq_base, SDV_NUM_PUB_GPIOS);
+
+ if (gpiochip_remove(&sd->bgpio.gc))
+ dev_err(&pdev->dev, "gpiochip_remove() failed.\n");
+
+ pci_release_region(pdev, GPIO_BAR);
+ iounmap(sd->gpio_pub_base);
+ pci_disable_device(pdev);
+ kfree(sd);
+}
+
+static struct pci_device_id sdv_gpio_pci_ids[] __devinitdata = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_SDV_GPIO) },
+ { 0, },
+};
+
+static struct pci_driver sdv_gpio_driver = {
+ .name = DRV_NAME,
+ .id_table = sdv_gpio_pci_ids,
+ .probe = sdv_gpio_probe,
+ .remove = sdv_gpio_remove,
+};
+
+static int __init sdv_gpio_init(void)
+{
+ return pci_register_driver(&sdv_gpio_driver);
+}
+module_init(sdv_gpio_init);
+
+static void __exit sdv_gpio_exit(void)
+{
+ pci_unregister_driver(&sdv_gpio_driver);
+}
+module_exit(sdv_gpio_exit);
+
+MODULE_AUTHOR("Hans J. Koch <hjk@linutronix.de>");
+MODULE_DESCRIPTION("GPIO interface for Intel Sodaville SoCs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c
index 87a68a8..dce3472 100644
--- a/drivers/gpio/gpio-stmpe.c
+++ b/drivers/gpio/gpio-stmpe.c
@@ -54,7 +54,7 @@ static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset)
if (ret < 0)
return ret;
- return ret & mask;
+ return !!(ret & mask);
}
static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
@@ -307,13 +307,11 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
struct stmpe_gpio_platform_data *pdata;
struct stmpe_gpio *stmpe_gpio;
int ret;
- int irq;
+ int irq = 0;
pdata = stmpe->pdata->gpio;
irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
stmpe_gpio = kzalloc(sizeof(struct stmpe_gpio), GFP_KERNEL);
if (!stmpe_gpio)
@@ -330,21 +328,28 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
stmpe_gpio->chip.dev = &pdev->dev;
stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1;
- stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0);
+ if (irq >= 0)
+ stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0);
+ else
+ dev_info(&pdev->dev,
+ "device configured in no-irq mode; "
+ "irqs are not available\n");
ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
if (ret)
goto out_free;
- ret = stmpe_gpio_irq_init(stmpe_gpio);
- if (ret)
- goto out_disable;
+ if (irq >= 0) {
+ ret = stmpe_gpio_irq_init(stmpe_gpio);
+ if (ret)
+ goto out_disable;
- ret = request_threaded_irq(irq, NULL, stmpe_gpio_irq, IRQF_ONESHOT,
- "stmpe-gpio", stmpe_gpio);
- if (ret) {
- dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
- goto out_removeirq;
+ ret = request_threaded_irq(irq, NULL, stmpe_gpio_irq,
+ IRQF_ONESHOT, "stmpe-gpio", stmpe_gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
+ goto out_removeirq;
+ }
}
ret = gpiochip_add(&stmpe_gpio->chip);
@@ -361,9 +366,11 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
return 0;
out_freeirq:
- free_irq(irq, stmpe_gpio);
+ if (irq >= 0)
+ free_irq(irq, stmpe_gpio);
out_removeirq:
- stmpe_gpio_irq_remove(stmpe_gpio);
+ if (irq >= 0)
+ stmpe_gpio_irq_remove(stmpe_gpio);
out_disable:
stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
out_free:
@@ -391,8 +398,10 @@ static int __devexit stmpe_gpio_remove(struct platform_device *pdev)
stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
- free_irq(irq, stmpe_gpio);
- stmpe_gpio_irq_remove(stmpe_gpio);
+ if (irq >= 0) {
+ free_irq(irq, stmpe_gpio);
+ stmpe_gpio_irq_remove(stmpe_gpio);
+ }
platform_set_drvdata(pdev, NULL);
kfree(stmpe_gpio);
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 6f17671..32de670 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -109,11 +109,13 @@ void tegra_gpio_enable(int gpio)
{
tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 1);
}
+EXPORT_SYMBOL_GPL(tegra_gpio_enable);
void tegra_gpio_disable(int gpio)
{
tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 0);
}
+EXPORT_SYMBOL_GPL(tegra_gpio_disable);
static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
@@ -459,7 +461,7 @@ static int __init tegra_gpio_init(void)
}
postcore_initcall(tegra_gpio_init);
-void __init tegra_gpio_config(struct tegra_gpio_table *table, int num)
+void tegra_gpio_config(struct tegra_gpio_table *table, int num)
{
int i;
diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c
index 91f45b9..7eef648 100644
--- a/drivers/gpio/gpio-tps65910.c
+++ b/drivers/gpio/gpio-tps65910.c
@@ -69,6 +69,7 @@ static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset)
void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base)
{
int ret;
+ struct tps65910_board *board_data;
if (!gpio_base)
return;
@@ -80,10 +81,10 @@ void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base)
switch(tps65910_chip_id(tps65910)) {
case TPS65910:
- tps65910->gpio.ngpio = 6;
+ tps65910->gpio.ngpio = TPS65910_NUM_GPIO;
break;
case TPS65911:
- tps65910->gpio.ngpio = 9;
+ tps65910->gpio.ngpio = TPS65911_NUM_GPIO;
break;
default:
return;
@@ -95,6 +96,21 @@ void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base)
tps65910->gpio.set = tps65910_gpio_set;
tps65910->gpio.get = tps65910_gpio_get;
+ /* Configure sleep control for gpios */
+ board_data = dev_get_platdata(tps65910->dev);
+ if (board_data) {
+ int i;
+ for (i = 0; i < tps65910->gpio.ngpio; ++i) {
+ if (board_data->en_gpio_sleep[i]) {
+ ret = tps65910_set_bits(tps65910,
+ TPS65910_GPIO0 + i, GPIO_SLEEP_MASK);
+ if (ret < 0)
+ dev_warn(tps65910->dev,
+ "GPIO Sleep setting failed\n");
+ }
+ }
+ }
+
ret = gpiochip_add(&tps65910->gpio);
if (ret)
diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c
index b8b4f22..94256fe 100644
--- a/drivers/gpio/gpio-twl4030.c
+++ b/drivers/gpio/gpio-twl4030.c
@@ -32,6 +32,8 @@
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/irqdomain.h>
#include <linux/i2c/twl.h>
@@ -256,7 +258,8 @@ static int twl_request(struct gpio_chip *chip, unsigned offset)
* and vMMC2 power supplies based on card presence.
*/
pdata = chip->dev->platform_data;
- value |= pdata->mmc_cd & 0x03;
+ if (pdata)
+ value |= pdata->mmc_cd & 0x03;
status = gpio_twl4030_write(REG_GPIO_CTRL, value);
}
@@ -395,59 +398,70 @@ static int gpio_twl4030_remove(struct platform_device *pdev);
static int __devinit gpio_twl4030_probe(struct platform_device *pdev)
{
struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data;
- int ret;
+ struct device_node *node = pdev->dev.of_node;
+ int ret, irq_base;
/* maybe setup IRQs */
- if (pdata->irq_base) {
- if (is_module()) {
- dev_err(&pdev->dev,
- "can't dispatch IRQs from modules\n");
- goto no_irqs;
- }
- ret = twl4030_sih_setup(TWL4030_MODULE_GPIO);
- if (ret < 0)
- return ret;
- WARN_ON(ret != pdata->irq_base);
- twl4030_gpio_irq_base = ret;
+ if (is_module()) {
+ dev_err(&pdev->dev, "can't dispatch IRQs from modules\n");
+ goto no_irqs;
+ }
+
+ irq_base = irq_alloc_descs(-1, 0, TWL4030_GPIO_MAX, 0);
+ if (irq_base < 0) {
+ dev_err(&pdev->dev, "Failed to alloc irq_descs\n");
+ return irq_base;
}
+ irq_domain_add_legacy(node, TWL4030_GPIO_MAX, irq_base, 0,
+ &irq_domain_simple_ops, NULL);
+
+ ret = twl4030_sih_setup(&pdev->dev, TWL4030_MODULE_GPIO, irq_base);
+ if (ret < 0)
+ return ret;
+
+ twl4030_gpio_irq_base = irq_base;
+
no_irqs:
- /*
- * NOTE: boards may waste power if they don't set pullups
- * and pulldowns correctly ... default for non-ULPI pins is
- * pulldown, and some other pins may have external pullups
- * or pulldowns. Careful!
- */
- ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns);
- if (ret)
- dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n",
- pdata->pullups, pdata->pulldowns,
- ret);
-
- ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd);
- if (ret)
- dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n",
- pdata->debounce, pdata->mmc_cd,
- ret);
-
- twl_gpiochip.base = pdata->gpio_base;
+ twl_gpiochip.base = -1;
twl_gpiochip.ngpio = TWL4030_GPIO_MAX;
twl_gpiochip.dev = &pdev->dev;
- /* NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE,
- * is (still) clear if use_leds is set.
- */
- if (pdata->use_leds)
- twl_gpiochip.ngpio += 2;
+ if (pdata) {
+ twl_gpiochip.base = pdata->gpio_base;
+
+ /*
+ * NOTE: boards may waste power if they don't set pullups
+ * and pulldowns correctly ... default for non-ULPI pins is
+ * pulldown, and some other pins may have external pullups
+ * or pulldowns. Careful!
+ */
+ ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns);
+ if (ret)
+ dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n",
+ pdata->pullups, pdata->pulldowns,
+ ret);
+
+ ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd);
+ if (ret)
+ dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n",
+ pdata->debounce, pdata->mmc_cd,
+ ret);
+
+ /*
+ * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE,
+ * is (still) clear if use_leds is set.
+ */
+ if (pdata->use_leds)
+ twl_gpiochip.ngpio += 2;
+ }
ret = gpiochip_add(&twl_gpiochip);
if (ret < 0) {
- dev_err(&pdev->dev,
- "could not register gpiochip, %d\n",
- ret);
+ dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);
twl_gpiochip.ngpio = 0;
gpio_twl4030_remove(pdev);
- } else if (pdata->setup) {
+ } else if (pdata && pdata->setup) {
int status;
status = pdata->setup(&pdev->dev,
@@ -465,7 +479,7 @@ static int gpio_twl4030_remove(struct platform_device *pdev)
struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data;
int status;
- if (pdata->teardown) {
+ if (pdata && pdata->teardown) {
status = pdata->teardown(&pdev->dev,
pdata->gpio_base, TWL4030_GPIO_MAX);
if (status) {
@@ -486,12 +500,21 @@ static int gpio_twl4030_remove(struct platform_device *pdev)
return -EIO;
}
+static const struct of_device_id twl_gpio_match[] = {
+ { .compatible = "ti,twl4030-gpio", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, twl_gpio_match);
+
/* Note: this hardware lives inside an I2C-based multi-function device. */
MODULE_ALIAS("platform:twl4030_gpio");
static struct platform_driver gpio_twl4030_driver = {
- .driver.name = "twl4030_gpio",
- .driver.owner = THIS_MODULE,
+ .driver = {
+ .name = "twl4030_gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(twl_gpio_match),
+ },
.probe = gpio_twl4030_probe,
.remove = gpio_twl4030_remove,
};
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 17fdf4b..5a75510 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -58,6 +58,8 @@ struct gpio_desc {
#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 FLAG_OPEN_DRAIN 8 /* Gpio is open drain type */
+#define FLAG_OPEN_SOURCE 9 /* Gpio is open source type */
#define ID_SHIFT 16 /* add new flags before this one */
@@ -873,6 +875,7 @@ void gpio_unexport(unsigned gpio)
{
struct gpio_desc *desc;
int status = 0;
+ struct device *dev = NULL;
if (!gpio_is_valid(gpio)) {
status = -EINVAL;
@@ -884,19 +887,20 @@ void gpio_unexport(unsigned gpio)
desc = &gpio_desc[gpio];
if (test_bit(FLAG_EXPORT, &desc->flags)) {
- struct device *dev = NULL;
dev = class_find_device(&gpio_class, NULL, desc, match_export);
if (dev) {
gpio_setup_irq(desc, dev, 0);
clear_bit(FLAG_EXPORT, &desc->flags);
- put_device(dev);
- device_unregister(dev);
} else
status = -ENODEV;
}
mutex_unlock(&sysfs_lock);
+ if (dev) {
+ device_unregister(dev);
+ put_device(dev);
+ }
done:
if (status)
pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
@@ -1150,8 +1154,9 @@ EXPORT_SYMBOL_GPL(gpiochip_remove);
* non-zero, this function will return to the caller and not iterate over any
* more gpio_chips.
*/
-struct gpio_chip *gpiochip_find(void *data,
- int (*match)(struct gpio_chip *chip, void *data))
+struct gpio_chip *gpiochip_find(const void *data,
+ int (*match)(struct gpio_chip *chip,
+ const void *data))
{
struct gpio_chip *chip = NULL;
unsigned long flags;
@@ -1261,6 +1266,8 @@ void gpio_free(unsigned gpio)
module_put(desc->chip->owner);
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
clear_bit(FLAG_REQUESTED, &desc->flags);
+ clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
+ clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
} else
WARN_ON(extra_checks);
@@ -1282,6 +1289,12 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
if (err)
return err;
+ if (flags & GPIOF_OPEN_DRAIN)
+ set_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags);
+
+ if (flags & GPIOF_OPEN_SOURCE)
+ set_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags);
+
if (flags & GPIOF_DIR_IN)
err = gpio_direction_input(gpio);
else
@@ -1431,6 +1444,14 @@ int gpio_direction_output(unsigned gpio, int value)
struct gpio_desc *desc = &gpio_desc[gpio];
int status = -EINVAL;
+ /* Open drain pin should not be driven to 1 */
+ if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags))
+ return gpio_direction_input(gpio);
+
+ /* Open source pin should not be driven to 0 */
+ if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags))
+ return gpio_direction_input(gpio);
+
spin_lock_irqsave(&gpio_lock, flags);
if (!gpio_is_valid(gpio))
@@ -1560,6 +1581,7 @@ int __gpio_get_value(unsigned gpio)
int value;
chip = gpio_to_chip(gpio);
+ /* Should be using gpio_get_value_cansleep() */
WARN_ON(chip->can_sleep);
value = chip->get ? chip->get(chip, gpio - chip->base) : 0;
trace_gpio_value(gpio, 1, value);
@@ -1567,6 +1589,57 @@ int __gpio_get_value(unsigned gpio)
}
EXPORT_SYMBOL_GPL(__gpio_get_value);
+/*
+ * _gpio_set_open_drain_value() - Set the open drain gpio's value.
+ * @gpio: Gpio whose state need to be set.
+ * @chip: Gpio chip.
+ * @value: Non-zero for setting it HIGH otherise it will set to LOW.
+ */
+static void _gpio_set_open_drain_value(unsigned gpio,
+ struct gpio_chip *chip, int value)
+{
+ int err = 0;
+ if (value) {
+ err = chip->direction_input(chip, gpio - chip->base);
+ if (!err)
+ clear_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags);
+ } else {
+ err = chip->direction_output(chip, gpio - chip->base, 0);
+ if (!err)
+ set_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags);
+ }
+ trace_gpio_direction(gpio, value, err);
+ if (err < 0)
+ pr_err("%s: Error in set_value for open drain gpio%d err %d\n",
+ __func__, gpio, err);
+}
+
+/*
+ * _gpio_set_open_source() - Set the open source gpio's value.
+ * @gpio: Gpio whose state need to be set.
+ * @chip: Gpio chip.
+ * @value: Non-zero for setting it HIGH otherise it will set to LOW.
+ */
+static void _gpio_set_open_source_value(unsigned gpio,
+ struct gpio_chip *chip, int value)
+{
+ int err = 0;
+ if (value) {
+ err = chip->direction_output(chip, gpio - chip->base, 1);
+ if (!err)
+ set_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags);
+ } else {
+ err = chip->direction_input(chip, gpio - chip->base);
+ if (!err)
+ clear_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags);
+ }
+ trace_gpio_direction(gpio, !value, err);
+ if (err < 0)
+ pr_err("%s: Error in set_value for open source gpio%d err %d\n",
+ __func__, gpio, err);
+}
+
+
/**
* __gpio_set_value() - assign a gpio's value
* @gpio: gpio whose value will be assigned
@@ -1581,9 +1654,15 @@ void __gpio_set_value(unsigned gpio, int value)
struct gpio_chip *chip;
chip = gpio_to_chip(gpio);
+ /* Should be using gpio_set_value_cansleep() */
WARN_ON(chip->can_sleep);
trace_gpio_value(gpio, 0, value);
- chip->set(chip, gpio - chip->base, value);
+ if (test_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags))
+ _gpio_set_open_drain_value(gpio, chip, value);
+ else if (test_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags))
+ _gpio_set_open_source_value(gpio, chip, value);
+ else
+ chip->set(chip, gpio - chip->base, value);
}
EXPORT_SYMBOL_GPL(__gpio_set_value);
@@ -1650,7 +1729,12 @@ void gpio_set_value_cansleep(unsigned gpio, int value)
might_sleep_if(extra_checks);
chip = gpio_to_chip(gpio);
trace_gpio_value(gpio, 0, value);
- chip->set(chip, gpio - chip->base, value);
+ if (test_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags))
+ _gpio_set_open_drain_value(gpio, chip, value);
+ else if (test_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags))
+ _gpio_set_open_source_value(gpio, chip, value);
+ else
+ chip->set(chip, gpio - chip->base, value);
}
EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
index 4c2cb4a..5675d93 100644
--- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
@@ -244,7 +244,6 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
uint64_t value)
{
struct drm_encoder *encoder = connector->encoder;
- struct backlight_device *psb_bd;
if (!strcmp(property->name, "scaling mode") && encoder) {
struct psb_intel_crtc *psb_crtc =
@@ -301,11 +300,15 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
value))
goto set_prop_error;
else {
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+ struct backlight_device *psb_bd;
+
psb_bd = mdfld_get_backlight_device();
if (psb_bd) {
psb_bd->props.brightness = value;
mdfld_set_brightness(psb_bd);
}
+#endif
}
}
set_prop_done:
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 0694e17..1a7559b 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -467,6 +467,10 @@ static int i915_drm_freeze(struct drm_device *dev)
/* Modeset on resume, not lid events */
dev_priv->modeset_on_lid = 0;
+ console_lock();
+ intel_fbdev_set_suspend(dev, 1);
+ console_unlock();
+
return 0;
}
@@ -539,6 +543,9 @@ static int i915_drm_thaw(struct drm_device *dev)
dev_priv->modeset_on_lid = 0;
+ console_lock();
+ intel_fbdev_set_suspend(dev, 0);
+ console_unlock();
return error;
}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 9cec6c3..5a14149 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -382,7 +382,7 @@ extern int intel_framebuffer_init(struct drm_device *dev,
struct drm_i915_gem_object *obj);
extern int intel_fbdev_init(struct drm_device *dev);
extern void intel_fbdev_fini(struct drm_device *dev);
-
+extern void intel_fbdev_set_suspend(struct drm_device *dev, int state);
extern void intel_prepare_page_flip(struct drm_device *dev, int plane);
extern void intel_finish_page_flip(struct drm_device *dev, int pipe);
extern void intel_finish_page_flip_plane(struct drm_device *dev, int plane);
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index 2d87669..19ecd78 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -254,6 +254,16 @@ void intel_fbdev_fini(struct drm_device *dev)
kfree(dev_priv->fbdev);
dev_priv->fbdev = NULL;
}
+
+void intel_fbdev_set_suspend(struct drm_device *dev, int state)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ if (!dev_priv->fbdev)
+ return;
+
+ fb_set_suspend(dev_priv->fbdev->helper.fbdev, state);
+}
+
MODULE_LICENSE("GPL and additional rights");
void intel_fb_output_poll_changed(struct drm_device *dev)
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 8f510fd..fa86035 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -654,10 +654,13 @@ nouveau_connector_detect_depth(struct drm_connector *connector)
if (nv_connector->edid && connector->display_info.bpc)
return;
- /* if not, we're out of options unless we're LVDS, default to 6bpc */
- connector->display_info.bpc = 6;
- if (nv_encoder->dcb->type != OUTPUT_LVDS)
+ /* if not, we're out of options unless we're LVDS, default to 8bpc */
+ if (nv_encoder->dcb->type != OUTPUT_LVDS) {
+ connector->display_info.bpc = 8;
return;
+ }
+
+ connector->display_info.bpc = 6;
/* LVDS: panel straps */
if (bios->fp_no_ddc) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
index 8f4f914..e2be95a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
@@ -315,8 +315,8 @@ nouveau_i2c_init(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvbios *bios = &dev_priv->vbios;
struct nouveau_i2c_chan *port;
+ u8 version = 0x00, entries, recordlen;
u8 *i2c, *entry, legacy[2][4] = {};
- u8 version, entries, recordlen;
int ret, i;
INIT_LIST_HEAD(&dev_priv->i2c_ports);
@@ -346,12 +346,12 @@ nouveau_i2c_init(struct drm_device *dev)
if (i2c[7]) legacy[1][1] = i2c[7];
}
- if (i2c && version >= 0x30) {
+ if (version >= 0x30) {
entry = i2c[1] + i2c;
entries = i2c[2];
recordlen = i2c[3];
} else
- if (i2c) {
+ if (version) {
entry = i2c;
entries = 16;
recordlen = 4;
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index a3ae91f..a4886b3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -852,7 +852,7 @@ nouveau_card_init(struct drm_device *dev)
if (ret)
goto out_pm;
- if (!dev_priv->noaccel) {
+ if (dev_priv->eng[NVOBJ_ENGINE_GR]) {
ret = nouveau_card_channel_init(dev);
if (ret)
goto out_fence;
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index 083b3ea..b5ff1f7 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -588,8 +588,8 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
if (encoder->crtc == crtc) {
radeon_encoder = to_radeon_encoder(encoder);
connector = radeon_get_connector_for_encoder(encoder);
- if (connector && connector->display_info.bpc)
- bpc = connector->display_info.bpc;
+ /* if (connector && connector->display_info.bpc)
+ bpc = connector->display_info.bpc; */
encoder_mode = atombios_get_encoder_mode(encoder);
is_duallink = radeon_dig_monitor_is_duallink(encoder, mode->clock);
if ((radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) ||
@@ -965,7 +965,9 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
struct radeon_connector_atom_dig *dig_connector =
radeon_connector->con_priv;
int dp_clock;
- bpc = connector->display_info.bpc;
+
+ /* if (connector->display_info.bpc)
+ bpc = connector->display_info.bpc; */
switch (encoder_mode) {
case ATOM_ENCODER_MODE_DP_MST:
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 6c62be2..c57d856 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -405,10 +405,13 @@ static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE],
/* get bpc from the EDID */
static int convert_bpc_to_bpp(int bpc)
{
+#if 0
if (bpc == 0)
return 24;
else
return bpc * 3;
+#endif
+ return 24;
}
/* get the max pix clock supported by the link rate and lane num */
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index 468b874..e607c4d 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -541,7 +541,7 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mo
dp_clock = dig_connector->dp_clock;
dp_lane_count = dig_connector->dp_lane_count;
hpd_id = radeon_connector->hpd.hpd;
- bpc = connector->display_info.bpc;
+ /* bpc = connector->display_info.bpc; */
}
/* no dig encoder assigned */
@@ -1159,7 +1159,7 @@ atombios_external_encoder_setup(struct drm_encoder *encoder,
dp_lane_count = dig_connector->dp_lane_count;
connector_object_id =
(radeon_connector->connector_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
- bpc = connector->display_info.bpc;
+ /* bpc = connector->display_info.bpc; */
}
memset(&args, 0, sizeof(args));
diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c
index a58b37a..70089d3 100644
--- a/drivers/gpu/drm/radeon/evergreen_cs.c
+++ b/drivers/gpu/drm/radeon/evergreen_cs.c
@@ -80,6 +80,9 @@ struct evergreen_cs_track {
bool cb_dirty;
bool db_dirty;
bool streamout_dirty;
+ u32 htile_offset;
+ u32 htile_surface;
+ struct radeon_bo *htile_bo;
};
static u32 evergreen_cs_get_aray_mode(u32 tiling_flags)
@@ -144,6 +147,9 @@ static void evergreen_cs_track_init(struct evergreen_cs_track *track)
track->db_s_read_bo = NULL;
track->db_s_write_bo = NULL;
track->db_dirty = true;
+ track->htile_bo = NULL;
+ track->htile_offset = 0xFFFFFFFF;
+ track->htile_surface = 0;
for (i = 0; i < 4; i++) {
track->vgt_strmout_size[i] = 0;
@@ -444,6 +450,62 @@ static int evergreen_cs_track_validate_cb(struct radeon_cs_parser *p, unsigned i
return 0;
}
+static int evergreen_cs_track_validate_htile(struct radeon_cs_parser *p,
+ unsigned nbx, unsigned nby)
+{
+ struct evergreen_cs_track *track = p->track;
+ unsigned long size;
+
+ if (track->htile_bo == NULL) {
+ dev_warn(p->dev, "%s:%d htile enabled without htile surface 0x%08x\n",
+ __func__, __LINE__, track->db_z_info);
+ return -EINVAL;
+ }
+
+ if (G_028ABC_LINEAR(track->htile_surface)) {
+ /* pitch must be 16 htiles aligned == 16 * 8 pixel aligned */
+ nbx = round_up(nbx, 16 * 8);
+ /* height is npipes htiles aligned == npipes * 8 pixel aligned */
+ nby = round_up(nby, track->npipes * 8);
+ } else {
+ switch (track->npipes) {
+ case 8:
+ nbx = round_up(nbx, 64 * 8);
+ nby = round_up(nby, 64 * 8);
+ break;
+ case 4:
+ nbx = round_up(nbx, 64 * 8);
+ nby = round_up(nby, 32 * 8);
+ break;
+ case 2:
+ nbx = round_up(nbx, 32 * 8);
+ nby = round_up(nby, 32 * 8);
+ break;
+ case 1:
+ nbx = round_up(nbx, 32 * 8);
+ nby = round_up(nby, 16 * 8);
+ break;
+ default:
+ dev_warn(p->dev, "%s:%d invalid num pipes %d\n",
+ __func__, __LINE__, track->npipes);
+ return -EINVAL;
+ }
+ }
+ /* compute number of htile */
+ nbx = nbx / 8;
+ nby = nby / 8;
+ size = nbx * nby * 4;
+ size += track->htile_offset;
+
+ if (size > radeon_bo_size(track->htile_bo)) {
+ dev_warn(p->dev, "%s:%d htile surface too small %ld for %ld (%d %d)\n",
+ __func__, __LINE__, radeon_bo_size(track->htile_bo),
+ size, nbx, nby);
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int evergreen_cs_track_validate_stencil(struct radeon_cs_parser *p)
{
struct evergreen_cs_track *track = p->track;
@@ -530,6 +592,14 @@ static int evergreen_cs_track_validate_stencil(struct radeon_cs_parser *p)
return -EINVAL;
}
+ /* hyperz */
+ if (G_028040_TILE_SURFACE_ENABLE(track->db_z_info)) {
+ r = evergreen_cs_track_validate_htile(p, surf.nbx, surf.nby);
+ if (r) {
+ return r;
+ }
+ }
+
return 0;
}
@@ -617,6 +687,14 @@ static int evergreen_cs_track_validate_depth(struct radeon_cs_parser *p)
return -EINVAL;
}
+ /* hyperz */
+ if (G_028040_TILE_SURFACE_ENABLE(track->db_z_info)) {
+ r = evergreen_cs_track_validate_htile(p, surf.nbx, surf.nby);
+ if (r) {
+ return r;
+ }
+ }
+
return 0;
}
@@ -850,7 +928,7 @@ static int evergreen_cs_track_check(struct radeon_cs_parser *p)
return r;
}
/* Check depth buffer */
- if (G_028800_Z_WRITE_ENABLE(track->db_depth_control)) {
+ if (G_028800_Z_ENABLE(track->db_depth_control)) {
r = evergreen_cs_track_validate_depth(p);
if (r)
return r;
@@ -1616,6 +1694,23 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->cb_color_bo[tmp] = reloc->robj;
track->cb_dirty = true;
break;
+ case DB_HTILE_DATA_BASE:
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "bad SET_CONTEXT_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ track->htile_offset = radeon_get_ib_value(p, idx);
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ track->htile_bo = reloc->robj;
+ track->db_dirty = true;
+ break;
+ case DB_HTILE_SURFACE:
+ /* 8x8 only */
+ track->htile_surface = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
+ break;
case CB_IMMED0_BASE:
case CB_IMMED1_BASE:
case CB_IMMED2_BASE:
@@ -1628,7 +1723,6 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_IMMED9_BASE:
case CB_IMMED10_BASE:
case CB_IMMED11_BASE:
- case DB_HTILE_DATA_BASE:
case SQ_PGM_START_FS:
case SQ_PGM_START_ES:
case SQ_PGM_START_VS:
diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h
index eb5708c..b4eefc3 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -991,6 +991,14 @@
#define G_028008_SLICE_MAX(x) (((x) >> 13) & 0x7FF)
#define C_028008_SLICE_MAX 0xFF001FFF
#define DB_HTILE_DATA_BASE 0x28014
+#define DB_HTILE_SURFACE 0x28abc
+#define S_028ABC_HTILE_WIDTH(x) (((x) & 0x1) << 0)
+#define G_028ABC_HTILE_WIDTH(x) (((x) >> 0) & 0x1)
+#define C_028ABC_HTILE_WIDTH 0xFFFFFFFE
+#define S_028ABC_HTILE_HEIGHT(x) (((x) & 0x1) << 1)
+#define G_028ABC_HTILE_HEIGHT(x) (((x) >> 1) & 0x1)
+#define C_028ABC_HTILE_HEIGHT 0xFFFFFFFD
+#define G_028ABC_LINEAR(x) (((x) >> 2) & 0x1)
#define DB_Z_INFO 0x28040
# define Z_ARRAY_MODE(x) ((x) << 4)
# define DB_TILE_SPLIT(x) (((x) & 0x7) << 8)
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index 0ec3f20..b8e12af 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -78,6 +78,9 @@ struct r600_cs_track {
bool cb_dirty;
bool db_dirty;
bool streamout_dirty;
+ struct radeon_bo *htile_bo;
+ u64 htile_offset;
+ u32 htile_surface;
};
#define FMT_8_BIT(fmt, vc) [fmt] = { 1, 1, 1, vc, CHIP_R600 }
@@ -321,6 +324,9 @@ static void r600_cs_track_init(struct r600_cs_track *track)
track->db_depth_size_idx = 0;
track->db_depth_control = 0xFFFFFFFF;
track->db_dirty = true;
+ track->htile_bo = NULL;
+ track->htile_offset = 0xFFFFFFFF;
+ track->htile_surface = 0;
for (i = 0; i < 4; i++) {
track->vgt_strmout_size[i] = 0;
@@ -455,12 +461,256 @@ static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
return 0;
}
+static int r600_cs_track_validate_db(struct radeon_cs_parser *p)
+{
+ struct r600_cs_track *track = p->track;
+ u32 nviews, bpe, ntiles, size, slice_tile_max, tmp;
+ u32 height_align, pitch_align, depth_align;
+ u32 pitch = 8192;
+ u32 height = 8192;
+ u64 base_offset, base_align;
+ struct array_mode_checker array_check;
+ int array_mode;
+ volatile u32 *ib = p->ib->ptr;
+
+
+ if (track->db_bo == NULL) {
+ dev_warn(p->dev, "z/stencil with no depth buffer\n");
+ return -EINVAL;
+ }
+ switch (G_028010_FORMAT(track->db_depth_info)) {
+ case V_028010_DEPTH_16:
+ bpe = 2;
+ break;
+ case V_028010_DEPTH_X8_24:
+ case V_028010_DEPTH_8_24:
+ case V_028010_DEPTH_X8_24_FLOAT:
+ case V_028010_DEPTH_8_24_FLOAT:
+ case V_028010_DEPTH_32_FLOAT:
+ bpe = 4;
+ break;
+ case V_028010_DEPTH_X24_8_32_FLOAT:
+ bpe = 8;
+ break;
+ default:
+ dev_warn(p->dev, "z/stencil with invalid format %d\n", G_028010_FORMAT(track->db_depth_info));
+ return -EINVAL;
+ }
+ if ((track->db_depth_size & 0xFFFFFC00) == 0xFFFFFC00) {
+ if (!track->db_depth_size_idx) {
+ dev_warn(p->dev, "z/stencil buffer size not set\n");
+ return -EINVAL;
+ }
+ tmp = radeon_bo_size(track->db_bo) - track->db_offset;
+ tmp = (tmp / bpe) >> 6;
+ if (!tmp) {
+ dev_warn(p->dev, "z/stencil buffer too small (0x%08X %d %d %ld)\n",
+ track->db_depth_size, bpe, track->db_offset,
+ radeon_bo_size(track->db_bo));
+ return -EINVAL;
+ }
+ ib[track->db_depth_size_idx] = S_028000_SLICE_TILE_MAX(tmp - 1) | (track->db_depth_size & 0x3FF);
+ } else {
+ size = radeon_bo_size(track->db_bo);
+ /* pitch in pixels */
+ pitch = (G_028000_PITCH_TILE_MAX(track->db_depth_size) + 1) * 8;
+ slice_tile_max = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1;
+ slice_tile_max *= 64;
+ height = slice_tile_max / pitch;
+ if (height > 8192)
+ height = 8192;
+ base_offset = track->db_bo_mc + track->db_offset;
+ array_mode = G_028010_ARRAY_MODE(track->db_depth_info);
+ array_check.array_mode = array_mode;
+ array_check.group_size = track->group_size;
+ array_check.nbanks = track->nbanks;
+ array_check.npipes = track->npipes;
+ array_check.nsamples = track->nsamples;
+ array_check.blocksize = bpe;
+ if (r600_get_array_mode_alignment(&array_check,
+ &pitch_align, &height_align, &depth_align, &base_align)) {
+ dev_warn(p->dev, "%s invalid tiling %d (0x%08X)\n", __func__,
+ G_028010_ARRAY_MODE(track->db_depth_info),
+ track->db_depth_info);
+ return -EINVAL;
+ }
+ switch (array_mode) {
+ case V_028010_ARRAY_1D_TILED_THIN1:
+ /* don't break userspace */
+ height &= ~0x7;
+ break;
+ case V_028010_ARRAY_2D_TILED_THIN1:
+ break;
+ default:
+ dev_warn(p->dev, "%s invalid tiling %d (0x%08X)\n", __func__,
+ G_028010_ARRAY_MODE(track->db_depth_info),
+ track->db_depth_info);
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(pitch, pitch_align)) {
+ dev_warn(p->dev, "%s:%d db pitch (%d, 0x%x, %d) invalid\n",
+ __func__, __LINE__, pitch, pitch_align, array_mode);
+ return -EINVAL;
+ }
+ if (!IS_ALIGNED(height, height_align)) {
+ dev_warn(p->dev, "%s:%d db height (%d, 0x%x, %d) invalid\n",
+ __func__, __LINE__, height, height_align, array_mode);
+ return -EINVAL;
+ }
+ if (!IS_ALIGNED(base_offset, base_align)) {
+ dev_warn(p->dev, "%s offset 0x%llx, 0x%llx, %d not aligned\n", __func__,
+ base_offset, base_align, array_mode);
+ return -EINVAL;
+ }
+
+ ntiles = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1;
+ nviews = G_028004_SLICE_MAX(track->db_depth_view) + 1;
+ tmp = ntiles * bpe * 64 * nviews;
+ if ((tmp + track->db_offset) > radeon_bo_size(track->db_bo)) {
+ dev_warn(p->dev, "z/stencil buffer (%d) too small (0x%08X %d %d %d -> %u have %lu)\n",
+ array_mode,
+ track->db_depth_size, ntiles, nviews, bpe, tmp + track->db_offset,
+ radeon_bo_size(track->db_bo));
+ return -EINVAL;
+ }
+ }
+
+ /* hyperz */
+ if (G_028010_TILE_SURFACE_ENABLE(track->db_depth_info)) {
+ unsigned long size;
+ unsigned nbx, nby;
+
+ if (track->htile_bo == NULL) {
+ dev_warn(p->dev, "%s:%d htile enabled without htile surface 0x%08x\n",
+ __func__, __LINE__, track->db_depth_info);
+ return -EINVAL;
+ }
+ if ((track->db_depth_size & 0xFFFFFC00) == 0xFFFFFC00) {
+ dev_warn(p->dev, "%s:%d htile can't be enabled with bogus db_depth_size 0x%08x\n",
+ __func__, __LINE__, track->db_depth_size);
+ return -EINVAL;
+ }
+
+ nbx = pitch;
+ nby = height;
+ if (G_028D24_LINEAR(track->htile_surface)) {
+ /* nbx must be 16 htiles aligned == 16 * 8 pixel aligned */
+ nbx = round_up(nbx, 16 * 8);
+ /* nby is npipes htiles aligned == npipes * 8 pixel aligned */
+ nby = round_up(nby, track->npipes * 8);
+ } else {
+ /* htile widht & nby (8 or 4) make 2 bits number */
+ tmp = track->htile_surface & 3;
+ /* align is htile align * 8, htile align vary according to
+ * number of pipe and tile width and nby
+ */
+ switch (track->npipes) {
+ case 8:
+ switch (tmp) {
+ case 3: /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/
+ nbx = round_up(nbx, 64 * 8);
+ nby = round_up(nby, 64 * 8);
+ break;
+ case 2: /* HTILE_WIDTH = 4 & HTILE_HEIGHT = 8*/
+ case 1: /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 4*/
+ nbx = round_up(nbx, 64 * 8);
+ nby = round_up(nby, 32 * 8);
+ break;
+ case 0: /* HTILE_WIDTH = 4 & HTILE_HEIGHT = 4*/
+ nbx = round_up(nbx, 32 * 8);
+ nby = round_up(nby, 32 * 8);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case 4:
+ switch (tmp) {
+ case 3: /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/
+ nbx = round_up(nbx, 64 * 8);
+ nby = round_up(nby, 32 * 8);
+ break;
+ case 2: /* HTILE_WIDTH = 4 & HTILE_HEIGHT = 8*/
+ case 1: /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 4*/
+ nbx = round_up(nbx, 32 * 8);
+ nby = round_up(nby, 32 * 8);
+ break;
+ case 0: /* HTILE_WIDTH = 4 & HTILE_HEIGHT = 4*/
+ nbx = round_up(nbx, 32 * 8);
+ nby = round_up(nby, 16 * 8);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case 2:
+ switch (tmp) {
+ case 3: /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/
+ nbx = round_up(nbx, 32 * 8);
+ nby = round_up(nby, 32 * 8);
+ break;
+ case 2: /* HTILE_WIDTH = 4 & HTILE_HEIGHT = 8*/
+ case 1: /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 4*/
+ nbx = round_up(nbx, 32 * 8);
+ nby = round_up(nby, 16 * 8);
+ break;
+ case 0: /* HTILE_WIDTH = 4 & HTILE_HEIGHT = 4*/
+ nbx = round_up(nbx, 16 * 8);
+ nby = round_up(nby, 16 * 8);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case 1:
+ switch (tmp) {
+ case 3: /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 8*/
+ nbx = round_up(nbx, 32 * 8);
+ nby = round_up(nby, 16 * 8);
+ break;
+ case 2: /* HTILE_WIDTH = 4 & HTILE_HEIGHT = 8*/
+ case 1: /* HTILE_WIDTH = 8 & HTILE_HEIGHT = 4*/
+ nbx = round_up(nbx, 16 * 8);
+ nby = round_up(nby, 16 * 8);
+ break;
+ case 0: /* HTILE_WIDTH = 4 & HTILE_HEIGHT = 4*/
+ nbx = round_up(nbx, 16 * 8);
+ nby = round_up(nby, 8 * 8);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_warn(p->dev, "%s:%d invalid num pipes %d\n",
+ __func__, __LINE__, track->npipes);
+ return -EINVAL;
+ }
+ }
+ /* compute number of htile */
+ nbx = G_028D24_HTILE_WIDTH(track->htile_surface) ? nbx / 8 : nbx / 4;
+ nby = G_028D24_HTILE_HEIGHT(track->htile_surface) ? nby / 8 : nby / 4;
+ size = nbx * nby * 4;
+ size += track->htile_offset;
+
+ if (size > radeon_bo_size(track->htile_bo)) {
+ dev_warn(p->dev, "%s:%d htile surface too small %ld for %ld (%d %d)\n",
+ __func__, __LINE__, radeon_bo_size(track->htile_bo),
+ size, nbx, nby);
+ return -EINVAL;
+ }
+ }
+
+ track->db_dirty = false;
+ return 0;
+}
+
static int r600_cs_track_check(struct radeon_cs_parser *p)
{
struct r600_cs_track *track = p->track;
u32 tmp;
int r, i;
- volatile u32 *ib = p->ib->ptr;
/* on legacy kernel we don't perform advanced check */
if (p->rdev == NULL)
@@ -513,124 +763,14 @@ static int r600_cs_track_check(struct radeon_cs_parser *p)
track->cb_dirty = false;
}
- if (track->db_dirty) {
- /* Check depth buffer */
- if (G_028800_STENCIL_ENABLE(track->db_depth_control) ||
- G_028800_Z_ENABLE(track->db_depth_control)) {
- u32 nviews, bpe, ntiles, size, slice_tile_max;
- u32 height, height_align, pitch, pitch_align, depth_align;
- u64 base_offset, base_align;
- struct array_mode_checker array_check;
- int array_mode;
-
- if (track->db_bo == NULL) {
- dev_warn(p->dev, "z/stencil with no depth buffer\n");
- return -EINVAL;
- }
- if (G_028010_TILE_SURFACE_ENABLE(track->db_depth_info)) {
- dev_warn(p->dev, "this kernel doesn't support z/stencil htile\n");
- return -EINVAL;
- }
- switch (G_028010_FORMAT(track->db_depth_info)) {
- case V_028010_DEPTH_16:
- bpe = 2;
- break;
- case V_028010_DEPTH_X8_24:
- case V_028010_DEPTH_8_24:
- case V_028010_DEPTH_X8_24_FLOAT:
- case V_028010_DEPTH_8_24_FLOAT:
- case V_028010_DEPTH_32_FLOAT:
- bpe = 4;
- break;
- case V_028010_DEPTH_X24_8_32_FLOAT:
- bpe = 8;
- break;
- default:
- dev_warn(p->dev, "z/stencil with invalid format %d\n", G_028010_FORMAT(track->db_depth_info));
- return -EINVAL;
- }
- if ((track->db_depth_size & 0xFFFFFC00) == 0xFFFFFC00) {
- if (!track->db_depth_size_idx) {
- dev_warn(p->dev, "z/stencil buffer size not set\n");
- return -EINVAL;
- }
- tmp = radeon_bo_size(track->db_bo) - track->db_offset;
- tmp = (tmp / bpe) >> 6;
- if (!tmp) {
- dev_warn(p->dev, "z/stencil buffer too small (0x%08X %d %d %ld)\n",
- track->db_depth_size, bpe, track->db_offset,
- radeon_bo_size(track->db_bo));
- return -EINVAL;
- }
- ib[track->db_depth_size_idx] = S_028000_SLICE_TILE_MAX(tmp - 1) | (track->db_depth_size & 0x3FF);
- } else {
- size = radeon_bo_size(track->db_bo);
- /* pitch in pixels */
- pitch = (G_028000_PITCH_TILE_MAX(track->db_depth_size) + 1) * 8;
- slice_tile_max = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1;
- slice_tile_max *= 64;
- height = slice_tile_max / pitch;
- if (height > 8192)
- height = 8192;
- base_offset = track->db_bo_mc + track->db_offset;
- array_mode = G_028010_ARRAY_MODE(track->db_depth_info);
- array_check.array_mode = array_mode;
- array_check.group_size = track->group_size;
- array_check.nbanks = track->nbanks;
- array_check.npipes = track->npipes;
- array_check.nsamples = track->nsamples;
- array_check.blocksize = bpe;
- if (r600_get_array_mode_alignment(&array_check,
- &pitch_align, &height_align, &depth_align, &base_align)) {
- dev_warn(p->dev, "%s invalid tiling %d (0x%08X)\n", __func__,
- G_028010_ARRAY_MODE(track->db_depth_info),
- track->db_depth_info);
- return -EINVAL;
- }
- switch (array_mode) {
- case V_028010_ARRAY_1D_TILED_THIN1:
- /* don't break userspace */
- height &= ~0x7;
- break;
- case V_028010_ARRAY_2D_TILED_THIN1:
- break;
- default:
- dev_warn(p->dev, "%s invalid tiling %d (0x%08X)\n", __func__,
- G_028010_ARRAY_MODE(track->db_depth_info),
- track->db_depth_info);
- return -EINVAL;
- }
-
- if (!IS_ALIGNED(pitch, pitch_align)) {
- dev_warn(p->dev, "%s:%d db pitch (%d, 0x%x, %d) invalid\n",
- __func__, __LINE__, pitch, pitch_align, array_mode);
- return -EINVAL;
- }
- if (!IS_ALIGNED(height, height_align)) {
- dev_warn(p->dev, "%s:%d db height (%d, 0x%x, %d) invalid\n",
- __func__, __LINE__, height, height_align, array_mode);
- return -EINVAL;
- }
- if (!IS_ALIGNED(base_offset, base_align)) {
- dev_warn(p->dev, "%s offset[%d] 0x%llx, 0x%llx, %d not aligned\n", __func__, i,
- base_offset, base_align, array_mode);
- return -EINVAL;
- }
-
- ntiles = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1;
- nviews = G_028004_SLICE_MAX(track->db_depth_view) + 1;
- tmp = ntiles * bpe * 64 * nviews;
- if ((tmp + track->db_offset) > radeon_bo_size(track->db_bo)) {
- dev_warn(p->dev, "z/stencil buffer (%d) too small (0x%08X %d %d %d -> %u have %lu)\n",
- array_mode,
- track->db_depth_size, ntiles, nviews, bpe, tmp + track->db_offset,
- radeon_bo_size(track->db_bo));
- return -EINVAL;
- }
- }
- }
- track->db_dirty = false;
+ /* Check depth buffer */
+ if (track->db_dirty && (G_028800_STENCIL_ENABLE(track->db_depth_control) ||
+ G_028800_Z_ENABLE(track->db_depth_control))) {
+ r = r600_cs_track_validate_db(p);
+ if (r)
+ return r;
}
+
return 0;
}
@@ -1244,6 +1384,21 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->db_dirty = true;
break;
case DB_HTILE_DATA_BASE:
+ r = r600_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "bad SET_CONTEXT_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ track->htile_offset = radeon_get_ib_value(p, idx) << 8;
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ track->htile_bo = reloc->robj;
+ track->db_dirty = true;
+ break;
+ case DB_HTILE_SURFACE:
+ track->htile_surface = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
+ break;
case SQ_PGM_START_FS:
case SQ_PGM_START_ES:
case SQ_PGM_START_VS:
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
index 3568a2e..59f9c99 100644
--- a/drivers/gpu/drm/radeon/r600d.h
+++ b/drivers/gpu/drm/radeon/r600d.h
@@ -195,6 +195,14 @@
#define PREZ_MUST_WAIT_FOR_POSTZ_DONE (1 << 31)
#define DB_DEPTH_BASE 0x2800C
#define DB_HTILE_DATA_BASE 0x28014
+#define DB_HTILE_SURFACE 0x28D24
+#define S_028D24_HTILE_WIDTH(x) (((x) & 0x1) << 0)
+#define G_028D24_HTILE_WIDTH(x) (((x) >> 0) & 0x1)
+#define C_028D24_HTILE_WIDTH 0xFFFFFFFE
+#define S_028D24_HTILE_HEIGHT(x) (((x) & 0x1) << 1)
+#define G_028D24_HTILE_HEIGHT(x) (((x) >> 1) & 0x1)
+#define C_028D24_HTILE_HEIGHT 0xFFFFFFFD
+#define G_028D24_LINEAR(x) (((x) >> 2) & 0x1)
#define DB_WATERMARKS 0x9838
#define DEPTH_FREE(x) ((x) << 0)
#define DEPTH_FLUSH(x) ((x) << 5)
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index 91541e6..6f70158 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -233,7 +233,17 @@ int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset,
bo->pin_count++;
if (gpu_addr)
*gpu_addr = radeon_bo_gpu_offset(bo);
- WARN_ON_ONCE(max_offset != 0);
+
+ if (max_offset != 0) {
+ u64 domain_start;
+
+ if (domain == RADEON_GEM_DOMAIN_VRAM)
+ domain_start = bo->rdev->mc.vram_start;
+ else
+ domain_start = bo->rdev->mc.gtt_start;
+ WARN_ON_ONCE((*gpu_addr - domain_start) > max_offset);
+ }
+
return 0;
}
radeon_ttm_placement_from_domain(bo, domain);
diff --git a/drivers/gpu/drm/radeon/reg_srcs/cayman b/drivers/gpu/drm/radeon/reg_srcs/cayman
index aea63c4..0f656b1 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/cayman
+++ b/drivers/gpu/drm/radeon/reg_srcs/cayman
@@ -509,7 +509,6 @@ cayman 0x9400
0x00028AA8 IA_MULTI_VGT_PARAM
0x00028AB4 VGT_REUSE_OFF
0x00028AB8 VGT_VTX_CNT_EN
-0x00028ABC DB_HTILE_SURFACE
0x00028AC0 DB_SRESULTS_COMPARE_STATE0
0x00028AC4 DB_SRESULTS_COMPARE_STATE1
0x00028AC8 DB_PRELOAD_CONTROL
diff --git a/drivers/gpu/drm/radeon/reg_srcs/evergreen b/drivers/gpu/drm/radeon/reg_srcs/evergreen
index 77c3720..b912a37 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/evergreen
+++ b/drivers/gpu/drm/radeon/reg_srcs/evergreen
@@ -519,7 +519,6 @@ evergreen 0x9400
0x00028AA4 VGT_INSTANCE_STEP_RATE_1
0x00028AB4 VGT_REUSE_OFF
0x00028AB8 VGT_VTX_CNT_EN
-0x00028ABC DB_HTILE_SURFACE
0x00028AC0 DB_SRESULTS_COMPARE_STATE0
0x00028AC4 DB_SRESULTS_COMPARE_STATE1
0x00028AC8 DB_PRELOAD_CONTROL
diff --git a/drivers/gpu/drm/radeon/reg_srcs/r600 b/drivers/gpu/drm/radeon/reg_srcs/r600
index 626c24e..5e659b0 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/r600
+++ b/drivers/gpu/drm/radeon/reg_srcs/r600
@@ -713,7 +713,6 @@ r600 0x9400
0x0000A710 TD_VS_SAMPLER17_BORDER_RED
0x00009508 TA_CNTL_AUX
0x0002802C DB_DEPTH_CLEAR
-0x00028D24 DB_HTILE_SURFACE
0x00028D34 DB_PREFETCH_LIMIT
0x00028D30 DB_PRELOAD_CONTROL
0x00028D0C DB_RENDER_CONTROL
diff --git a/drivers/hsi/Kconfig b/drivers/hsi/Kconfig
new file mode 100644
index 0000000..d94e38d
--- /dev/null
+++ b/drivers/hsi/Kconfig
@@ -0,0 +1,19 @@
+#
+# HSI driver configuration
+#
+menuconfig HSI
+ tristate "HSI support"
+ ---help---
+ The "High speed synchronous Serial Interface" is
+ synchronous serial interface used mainly to connect
+ application engines and cellular modems.
+
+if HSI
+
+config HSI_BOARDINFO
+ bool
+ default y
+
+source "drivers/hsi/clients/Kconfig"
+
+endif # HSI
diff --git a/drivers/hsi/Makefile b/drivers/hsi/Makefile
new file mode 100644
index 0000000..9d5d33f
--- /dev/null
+++ b/drivers/hsi/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for HSI
+#
+obj-$(CONFIG_HSI_BOARDINFO) += hsi_boardinfo.o
+obj-$(CONFIG_HSI) += hsi.o
+obj-y += clients/
diff --git a/drivers/hsi/clients/Kconfig b/drivers/hsi/clients/Kconfig
new file mode 100644
index 0000000..3bacd27
--- /dev/null
+++ b/drivers/hsi/clients/Kconfig
@@ -0,0 +1,13 @@
+#
+# HSI clients configuration
+#
+
+comment "HSI clients"
+
+config HSI_CHAR
+ tristate "HSI/SSI character driver"
+ depends on HSI
+ ---help---
+ If you say Y here, you will enable the HSI/SSI character driver.
+ This driver provides a simple character device interface for
+ serial communication with the cellular modem over HSI/SSI bus.
diff --git a/drivers/hsi/clients/Makefile b/drivers/hsi/clients/Makefile
new file mode 100644
index 0000000..327c0e2
--- /dev/null
+++ b/drivers/hsi/clients/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for HSI clients
+#
+
+obj-$(CONFIG_HSI_CHAR) += hsi_char.o
diff --git a/drivers/hsi/clients/hsi_char.c b/drivers/hsi/clients/hsi_char.c
new file mode 100644
index 0000000..88a050d
--- /dev/null
+++ b/drivers/hsi/clients/hsi_char.c
@@ -0,0 +1,802 @@
+/*
+ * HSI character device driver, implements the character device
+ * interface.
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Andras Domokos <andras.domokos@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/kmemleak.h>
+#include <linux/ioctl.h>
+#include <linux/wait.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/scatterlist.h>
+#include <linux/stat.h>
+#include <linux/hsi/hsi.h>
+#include <linux/hsi/hsi_char.h>
+
+#define HSC_DEVS 16 /* Num of channels */
+#define HSC_MSGS 4
+
+#define HSC_RXBREAK 0
+
+#define HSC_ID_BITS 6
+#define HSC_PORT_ID_BITS 4
+#define HSC_ID_MASK 3
+#define HSC_PORT_ID_MASK 3
+#define HSC_CH_MASK 0xf
+
+/*
+ * We support up to 4 controllers that can have up to 4
+ * ports, which should currently be more than enough.
+ */
+#define HSC_BASEMINOR(id, port_id) \
+ ((((id) & HSC_ID_MASK) << HSC_ID_BITS) | \
+ (((port_id) & HSC_PORT_ID_MASK) << HSC_PORT_ID_BITS))
+
+enum {
+ HSC_CH_OPEN,
+ HSC_CH_READ,
+ HSC_CH_WRITE,
+ HSC_CH_WLINE,
+};
+
+enum {
+ HSC_RX,
+ HSC_TX,
+};
+
+struct hsc_client_data;
+/**
+ * struct hsc_channel - hsi_char internal channel data
+ * @ch: channel number
+ * @flags: Keeps state of the channel (open/close, reading, writing)
+ * @free_msgs_list: List of free HSI messages/requests
+ * @rx_msgs_queue: List of pending RX requests
+ * @tx_msgs_queue: List of pending TX requests
+ * @lock: Serialize access to the lists
+ * @cl: reference to the associated hsi_client
+ * @cl_data: reference to the client data that this channels belongs to
+ * @rx_wait: RX requests wait queue
+ * @tx_wait: TX requests wait queue
+ */
+struct hsc_channel {
+ unsigned int ch;
+ unsigned long flags;
+ struct list_head free_msgs_list;
+ struct list_head rx_msgs_queue;
+ struct list_head tx_msgs_queue;
+ spinlock_t lock;
+ struct hsi_client *cl;
+ struct hsc_client_data *cl_data;
+ wait_queue_head_t rx_wait;
+ wait_queue_head_t tx_wait;
+};
+
+/**
+ * struct hsc_client_data - hsi_char internal client data
+ * @cdev: Characther device associated to the hsi_client
+ * @lock: Lock to serialize open/close access
+ * @flags: Keeps track of port state (rx hwbreak armed)
+ * @usecnt: Use count for claiming the HSI port (mutex protected)
+ * @cl: Referece to the HSI client
+ * @channels: Array of channels accessible by the client
+ */
+struct hsc_client_data {
+ struct cdev cdev;
+ struct mutex lock;
+ unsigned long flags;
+ unsigned int usecnt;
+ struct hsi_client *cl;
+ struct hsc_channel channels[HSC_DEVS];
+};
+
+/* Stores the major number dynamically allocated for hsi_char */
+static unsigned int hsc_major;
+/* Maximum buffer size that hsi_char will accept from userspace */
+static unsigned int max_data_size = 0x1000;
+module_param(max_data_size, uint, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(max_data_size, "max read/write data size [4,8..65536] (^2)");
+
+static void hsc_add_tail(struct hsc_channel *channel, struct hsi_msg *msg,
+ struct list_head *queue)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&channel->lock, flags);
+ list_add_tail(&msg->link, queue);
+ spin_unlock_irqrestore(&channel->lock, flags);
+}
+
+static struct hsi_msg *hsc_get_first_msg(struct hsc_channel *channel,
+ struct list_head *queue)
+{
+ struct hsi_msg *msg = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&channel->lock, flags);
+
+ if (list_empty(queue))
+ goto out;
+
+ msg = list_first_entry(queue, struct hsi_msg, link);
+ list_del(&msg->link);
+out:
+ spin_unlock_irqrestore(&channel->lock, flags);
+
+ return msg;
+}
+
+static inline void hsc_msg_free(struct hsi_msg *msg)
+{
+ kfree(sg_virt(msg->sgt.sgl));
+ hsi_free_msg(msg);
+}
+
+static void hsc_free_list(struct list_head *list)
+{
+ struct hsi_msg *msg, *tmp;
+
+ list_for_each_entry_safe(msg, tmp, list, link) {
+ list_del(&msg->link);
+ hsc_msg_free(msg);
+ }
+}
+
+static void hsc_reset_list(struct hsc_channel *channel, struct list_head *l)
+{
+ unsigned long flags;
+ LIST_HEAD(list);
+
+ spin_lock_irqsave(&channel->lock, flags);
+ list_splice_init(l, &list);
+ spin_unlock_irqrestore(&channel->lock, flags);
+
+ hsc_free_list(&list);
+}
+
+static inline struct hsi_msg *hsc_msg_alloc(unsigned int alloc_size)
+{
+ struct hsi_msg *msg;
+ void *buf;
+
+ msg = hsi_alloc_msg(1, GFP_KERNEL);
+ if (!msg)
+ goto out;
+ buf = kmalloc(alloc_size, GFP_KERNEL);
+ if (!buf) {
+ hsi_free_msg(msg);
+ goto out;
+ }
+ sg_init_one(msg->sgt.sgl, buf, alloc_size);
+ /* Ignore false positive, due to sg pointer handling */
+ kmemleak_ignore(buf);
+
+ return msg;
+out:
+ return NULL;
+}
+
+static inline int hsc_msgs_alloc(struct hsc_channel *channel)
+{
+ struct hsi_msg *msg;
+ int i;
+
+ for (i = 0; i < HSC_MSGS; i++) {
+ msg = hsc_msg_alloc(max_data_size);
+ if (!msg)
+ goto out;
+ msg->channel = channel->ch;
+ list_add_tail(&msg->link, &channel->free_msgs_list);
+ }
+
+ return 0;
+out:
+ hsc_free_list(&channel->free_msgs_list);
+
+ return -ENOMEM;
+}
+
+static inline unsigned int hsc_msg_len_get(struct hsi_msg *msg)
+{
+ return msg->sgt.sgl->length;
+}
+
+static inline void hsc_msg_len_set(struct hsi_msg *msg, unsigned int len)
+{
+ msg->sgt.sgl->length = len;
+}
+
+static void hsc_rx_completed(struct hsi_msg *msg)
+{
+ struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl);
+ struct hsc_channel *channel = cl_data->channels + msg->channel;
+
+ if (test_bit(HSC_CH_READ, &channel->flags)) {
+ hsc_add_tail(channel, msg, &channel->rx_msgs_queue);
+ wake_up(&channel->rx_wait);
+ } else {
+ hsc_add_tail(channel, msg, &channel->free_msgs_list);
+ }
+}
+
+static void hsc_rx_msg_destructor(struct hsi_msg *msg)
+{
+ msg->status = HSI_STATUS_ERROR;
+ hsc_msg_len_set(msg, 0);
+ hsc_rx_completed(msg);
+}
+
+static void hsc_tx_completed(struct hsi_msg *msg)
+{
+ struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl);
+ struct hsc_channel *channel = cl_data->channels + msg->channel;
+
+ if (test_bit(HSC_CH_WRITE, &channel->flags)) {
+ hsc_add_tail(channel, msg, &channel->tx_msgs_queue);
+ wake_up(&channel->tx_wait);
+ } else {
+ hsc_add_tail(channel, msg, &channel->free_msgs_list);
+ }
+}
+
+static void hsc_tx_msg_destructor(struct hsi_msg *msg)
+{
+ msg->status = HSI_STATUS_ERROR;
+ hsc_msg_len_set(msg, 0);
+ hsc_tx_completed(msg);
+}
+
+static void hsc_break_req_destructor(struct hsi_msg *msg)
+{
+ struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl);
+
+ hsi_free_msg(msg);
+ clear_bit(HSC_RXBREAK, &cl_data->flags);
+}
+
+static void hsc_break_received(struct hsi_msg *msg)
+{
+ struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl);
+ struct hsc_channel *channel = cl_data->channels;
+ int i, ret;
+
+ /* Broadcast HWBREAK on all channels */
+ for (i = 0; i < HSC_DEVS; i++, channel++) {
+ struct hsi_msg *msg2;
+
+ if (!test_bit(HSC_CH_READ, &channel->flags))
+ continue;
+ msg2 = hsc_get_first_msg(channel, &channel->free_msgs_list);
+ if (!msg2)
+ continue;
+ clear_bit(HSC_CH_READ, &channel->flags);
+ hsc_msg_len_set(msg2, 0);
+ msg2->status = HSI_STATUS_COMPLETED;
+ hsc_add_tail(channel, msg2, &channel->rx_msgs_queue);
+ wake_up(&channel->rx_wait);
+ }
+ hsi_flush(msg->cl);
+ ret = hsi_async_read(msg->cl, msg);
+ if (ret < 0)
+ hsc_break_req_destructor(msg);
+}
+
+static int hsc_break_request(struct hsi_client *cl)
+{
+ struct hsc_client_data *cl_data = hsi_client_drvdata(cl);
+ struct hsi_msg *msg;
+ int ret;
+
+ if (test_and_set_bit(HSC_RXBREAK, &cl_data->flags))
+ return -EBUSY;
+
+ msg = hsi_alloc_msg(0, GFP_KERNEL);
+ if (!msg) {
+ clear_bit(HSC_RXBREAK, &cl_data->flags);
+ return -ENOMEM;
+ }
+ msg->break_frame = 1;
+ msg->complete = hsc_break_received;
+ msg->destructor = hsc_break_req_destructor;
+ ret = hsi_async_read(cl, msg);
+ if (ret < 0)
+ hsc_break_req_destructor(msg);
+
+ return ret;
+}
+
+static int hsc_break_send(struct hsi_client *cl)
+{
+ struct hsi_msg *msg;
+ int ret;
+
+ msg = hsi_alloc_msg(0, GFP_ATOMIC);
+ if (!msg)
+ return -ENOMEM;
+ msg->break_frame = 1;
+ msg->complete = hsi_free_msg;
+ msg->destructor = hsi_free_msg;
+ ret = hsi_async_write(cl, msg);
+ if (ret < 0)
+ hsi_free_msg(msg);
+
+ return ret;
+}
+
+static int hsc_rx_set(struct hsi_client *cl, struct hsc_rx_config *rxc)
+{
+ struct hsi_config tmp;
+ int ret;
+
+ if ((rxc->mode != HSI_MODE_STREAM) && (rxc->mode != HSI_MODE_FRAME))
+ return -EINVAL;
+ if ((rxc->channels == 0) || (rxc->channels > HSC_DEVS))
+ return -EINVAL;
+ if (rxc->channels & (rxc->channels - 1))
+ return -EINVAL;
+ if ((rxc->flow != HSI_FLOW_SYNC) && (rxc->flow != HSI_FLOW_PIPE))
+ return -EINVAL;
+ tmp = cl->rx_cfg;
+ cl->rx_cfg.mode = rxc->mode;
+ cl->rx_cfg.channels = rxc->channels;
+ cl->rx_cfg.flow = rxc->flow;
+ ret = hsi_setup(cl);
+ if (ret < 0) {
+ cl->rx_cfg = tmp;
+ return ret;
+ }
+ if (rxc->mode == HSI_MODE_FRAME)
+ hsc_break_request(cl);
+
+ return ret;
+}
+
+static inline void hsc_rx_get(struct hsi_client *cl, struct hsc_rx_config *rxc)
+{
+ rxc->mode = cl->rx_cfg.mode;
+ rxc->channels = cl->rx_cfg.channels;
+ rxc->flow = cl->rx_cfg.flow;
+}
+
+static int hsc_tx_set(struct hsi_client *cl, struct hsc_tx_config *txc)
+{
+ struct hsi_config tmp;
+ int ret;
+
+ if ((txc->mode != HSI_MODE_STREAM) && (txc->mode != HSI_MODE_FRAME))
+ return -EINVAL;
+ if ((txc->channels == 0) || (txc->channels > HSC_DEVS))
+ return -EINVAL;
+ if (txc->channels & (txc->channels - 1))
+ return -EINVAL;
+ if ((txc->arb_mode != HSI_ARB_RR) && (txc->arb_mode != HSI_ARB_PRIO))
+ return -EINVAL;
+ tmp = cl->tx_cfg;
+ cl->tx_cfg.mode = txc->mode;
+ cl->tx_cfg.channels = txc->channels;
+ cl->tx_cfg.speed = txc->speed;
+ cl->tx_cfg.arb_mode = txc->arb_mode;
+ ret = hsi_setup(cl);
+ if (ret < 0) {
+ cl->tx_cfg = tmp;
+ return ret;
+ }
+
+ return ret;
+}
+
+static inline void hsc_tx_get(struct hsi_client *cl, struct hsc_tx_config *txc)
+{
+ txc->mode = cl->tx_cfg.mode;
+ txc->channels = cl->tx_cfg.channels;
+ txc->speed = cl->tx_cfg.speed;
+ txc->arb_mode = cl->tx_cfg.arb_mode;
+}
+
+static ssize_t hsc_read(struct file *file, char __user *buf, size_t len,
+ loff_t *ppos __maybe_unused)
+{
+ struct hsc_channel *channel = file->private_data;
+ struct hsi_msg *msg;
+ ssize_t ret;
+
+ if (len == 0)
+ return 0;
+ if (!IS_ALIGNED(len, sizeof(u32)))
+ return -EINVAL;
+ if (len > max_data_size)
+ len = max_data_size;
+ if (channel->ch >= channel->cl->rx_cfg.channels)
+ return -ECHRNG;
+ if (test_and_set_bit(HSC_CH_READ, &channel->flags))
+ return -EBUSY;
+ msg = hsc_get_first_msg(channel, &channel->free_msgs_list);
+ if (!msg) {
+ ret = -ENOSPC;
+ goto out;
+ }
+ hsc_msg_len_set(msg, len);
+ msg->complete = hsc_rx_completed;
+ msg->destructor = hsc_rx_msg_destructor;
+ ret = hsi_async_read(channel->cl, msg);
+ if (ret < 0) {
+ hsc_add_tail(channel, msg, &channel->free_msgs_list);
+ goto out;
+ }
+
+ ret = wait_event_interruptible(channel->rx_wait,
+ !list_empty(&channel->rx_msgs_queue));
+ if (ret < 0) {
+ clear_bit(HSC_CH_READ, &channel->flags);
+ hsi_flush(channel->cl);
+ return -EINTR;
+ }
+
+ msg = hsc_get_first_msg(channel, &channel->rx_msgs_queue);
+ if (msg) {
+ if (msg->status != HSI_STATUS_ERROR) {
+ ret = copy_to_user((void __user *)buf,
+ sg_virt(msg->sgt.sgl), hsc_msg_len_get(msg));
+ if (ret)
+ ret = -EFAULT;
+ else
+ ret = hsc_msg_len_get(msg);
+ } else {
+ ret = -EIO;
+ }
+ hsc_add_tail(channel, msg, &channel->free_msgs_list);
+ }
+out:
+ clear_bit(HSC_CH_READ, &channel->flags);
+
+ return ret;
+}
+
+static ssize_t hsc_write(struct file *file, const char __user *buf, size_t len,
+ loff_t *ppos __maybe_unused)
+{
+ struct hsc_channel *channel = file->private_data;
+ struct hsi_msg *msg;
+ ssize_t ret;
+
+ if ((len == 0) || !IS_ALIGNED(len, sizeof(u32)))
+ return -EINVAL;
+ if (len > max_data_size)
+ len = max_data_size;
+ if (channel->ch >= channel->cl->tx_cfg.channels)
+ return -ECHRNG;
+ if (test_and_set_bit(HSC_CH_WRITE, &channel->flags))
+ return -EBUSY;
+ msg = hsc_get_first_msg(channel, &channel->free_msgs_list);
+ if (!msg) {
+ clear_bit(HSC_CH_WRITE, &channel->flags);
+ return -ENOSPC;
+ }
+ if (copy_from_user(sg_virt(msg->sgt.sgl), (void __user *)buf, len)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ hsc_msg_len_set(msg, len);
+ msg->complete = hsc_tx_completed;
+ msg->destructor = hsc_tx_msg_destructor;
+ ret = hsi_async_write(channel->cl, msg);
+ if (ret < 0)
+ goto out;
+
+ ret = wait_event_interruptible(channel->tx_wait,
+ !list_empty(&channel->tx_msgs_queue));
+ if (ret < 0) {
+ clear_bit(HSC_CH_WRITE, &channel->flags);
+ hsi_flush(channel->cl);
+ return -EINTR;
+ }
+
+ msg = hsc_get_first_msg(channel, &channel->tx_msgs_queue);
+ if (msg) {
+ if (msg->status == HSI_STATUS_ERROR)
+ ret = -EIO;
+ else
+ ret = hsc_msg_len_get(msg);
+
+ hsc_add_tail(channel, msg, &channel->free_msgs_list);
+ }
+out:
+ clear_bit(HSC_CH_WRITE, &channel->flags);
+
+ return ret;
+}
+
+static long hsc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct hsc_channel *channel = file->private_data;
+ unsigned int state;
+ struct hsc_rx_config rxc;
+ struct hsc_tx_config txc;
+ long ret = 0;
+
+ switch (cmd) {
+ case HSC_RESET:
+ hsi_flush(channel->cl);
+ break;
+ case HSC_SET_PM:
+ if (copy_from_user(&state, (void __user *)arg, sizeof(state)))
+ return -EFAULT;
+ if (state == HSC_PM_DISABLE) {
+ if (test_and_set_bit(HSC_CH_WLINE, &channel->flags))
+ return -EINVAL;
+ ret = hsi_start_tx(channel->cl);
+ } else if (state == HSC_PM_ENABLE) {
+ if (!test_and_clear_bit(HSC_CH_WLINE, &channel->flags))
+ return -EINVAL;
+ ret = hsi_stop_tx(channel->cl);
+ } else {
+ ret = -EINVAL;
+ }
+ break;
+ case HSC_SEND_BREAK:
+ return hsc_break_send(channel->cl);
+ case HSC_SET_RX:
+ if (copy_from_user(&rxc, (void __user *)arg, sizeof(rxc)))
+ return -EFAULT;
+ return hsc_rx_set(channel->cl, &rxc);
+ case HSC_GET_RX:
+ hsc_rx_get(channel->cl, &rxc);
+ if (copy_to_user((void __user *)arg, &rxc, sizeof(rxc)))
+ return -EFAULT;
+ break;
+ case HSC_SET_TX:
+ if (copy_from_user(&txc, (void __user *)arg, sizeof(txc)))
+ return -EFAULT;
+ return hsc_tx_set(channel->cl, &txc);
+ case HSC_GET_TX:
+ hsc_tx_get(channel->cl, &txc);
+ if (copy_to_user((void __user *)arg, &txc, sizeof(txc)))
+ return -EFAULT;
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return ret;
+}
+
+static inline void __hsc_port_release(struct hsc_client_data *cl_data)
+{
+ BUG_ON(cl_data->usecnt == 0);
+
+ if (--cl_data->usecnt == 0) {
+ hsi_flush(cl_data->cl);
+ hsi_release_port(cl_data->cl);
+ }
+}
+
+static int hsc_open(struct inode *inode, struct file *file)
+{
+ struct hsc_client_data *cl_data;
+ struct hsc_channel *channel;
+ int ret = 0;
+
+ pr_debug("open, minor = %d\n", iminor(inode));
+
+ cl_data = container_of(inode->i_cdev, struct hsc_client_data, cdev);
+ mutex_lock(&cl_data->lock);
+ channel = cl_data->channels + (iminor(inode) & HSC_CH_MASK);
+
+ if (test_and_set_bit(HSC_CH_OPEN, &channel->flags)) {
+ ret = -EBUSY;
+ goto out;
+ }
+ /*
+ * Check if we have already claimed the port associated to the HSI
+ * client. If not then try to claim it, else increase its refcount
+ */
+ if (cl_data->usecnt == 0) {
+ ret = hsi_claim_port(cl_data->cl, 0);
+ if (ret < 0)
+ goto out;
+ hsi_setup(cl_data->cl);
+ }
+ cl_data->usecnt++;
+
+ ret = hsc_msgs_alloc(channel);
+ if (ret < 0) {
+ __hsc_port_release(cl_data);
+ goto out;
+ }
+
+ file->private_data = channel;
+ mutex_unlock(&cl_data->lock);
+
+ return ret;
+out:
+ mutex_unlock(&cl_data->lock);
+
+ return ret;
+}
+
+static int hsc_release(struct inode *inode __maybe_unused, struct file *file)
+{
+ struct hsc_channel *channel = file->private_data;
+ struct hsc_client_data *cl_data = channel->cl_data;
+
+ mutex_lock(&cl_data->lock);
+ file->private_data = NULL;
+ if (test_and_clear_bit(HSC_CH_WLINE, &channel->flags))
+ hsi_stop_tx(channel->cl);
+ __hsc_port_release(cl_data);
+ hsc_reset_list(channel, &channel->rx_msgs_queue);
+ hsc_reset_list(channel, &channel->tx_msgs_queue);
+ hsc_reset_list(channel, &channel->free_msgs_list);
+ clear_bit(HSC_CH_READ, &channel->flags);
+ clear_bit(HSC_CH_WRITE, &channel->flags);
+ clear_bit(HSC_CH_OPEN, &channel->flags);
+ wake_up(&channel->rx_wait);
+ wake_up(&channel->tx_wait);
+ mutex_unlock(&cl_data->lock);
+
+ return 0;
+}
+
+static const struct file_operations hsc_fops = {
+ .owner = THIS_MODULE,
+ .read = hsc_read,
+ .write = hsc_write,
+ .unlocked_ioctl = hsc_ioctl,
+ .open = hsc_open,
+ .release = hsc_release,
+};
+
+static void __devinit hsc_channel_init(struct hsc_channel *channel)
+{
+ init_waitqueue_head(&channel->rx_wait);
+ init_waitqueue_head(&channel->tx_wait);
+ spin_lock_init(&channel->lock);
+ INIT_LIST_HEAD(&channel->free_msgs_list);
+ INIT_LIST_HEAD(&channel->rx_msgs_queue);
+ INIT_LIST_HEAD(&channel->tx_msgs_queue);
+}
+
+static int __devinit hsc_probe(struct device *dev)
+{
+ const char devname[] = "hsi_char";
+ struct hsc_client_data *cl_data;
+ struct hsc_channel *channel;
+ struct hsi_client *cl = to_hsi_client(dev);
+ unsigned int hsc_baseminor;
+ dev_t hsc_dev;
+ int ret;
+ int i;
+
+ cl_data = kzalloc(sizeof(*cl_data), GFP_KERNEL);
+ if (!cl_data) {
+ dev_err(dev, "Could not allocate hsc_client_data\n");
+ return -ENOMEM;
+ }
+ hsc_baseminor = HSC_BASEMINOR(hsi_id(cl), hsi_port_id(cl));
+ if (!hsc_major) {
+ ret = alloc_chrdev_region(&hsc_dev, hsc_baseminor,
+ HSC_DEVS, devname);
+ if (ret > 0)
+ hsc_major = MAJOR(hsc_dev);
+ } else {
+ hsc_dev = MKDEV(hsc_major, hsc_baseminor);
+ ret = register_chrdev_region(hsc_dev, HSC_DEVS, devname);
+ }
+ if (ret < 0) {
+ dev_err(dev, "Device %s allocation failed %d\n",
+ hsc_major ? "minor" : "major", ret);
+ goto out1;
+ }
+ mutex_init(&cl_data->lock);
+ hsi_client_set_drvdata(cl, cl_data);
+ cdev_init(&cl_data->cdev, &hsc_fops);
+ cl_data->cdev.owner = THIS_MODULE;
+ cl_data->cl = cl;
+ for (i = 0, channel = cl_data->channels; i < HSC_DEVS; i++, channel++) {
+ hsc_channel_init(channel);
+ channel->ch = i;
+ channel->cl = cl;
+ channel->cl_data = cl_data;
+ }
+
+ /* 1 hsi client -> N char devices (one for each channel) */
+ ret = cdev_add(&cl_data->cdev, hsc_dev, HSC_DEVS);
+ if (ret) {
+ dev_err(dev, "Could not add char device %d\n", ret);
+ goto out2;
+ }
+
+ return 0;
+out2:
+ unregister_chrdev_region(hsc_dev, HSC_DEVS);
+out1:
+ kfree(cl_data);
+
+ return ret;
+}
+
+static int __devexit hsc_remove(struct device *dev)
+{
+ struct hsi_client *cl = to_hsi_client(dev);
+ struct hsc_client_data *cl_data = hsi_client_drvdata(cl);
+ dev_t hsc_dev = cl_data->cdev.dev;
+
+ cdev_del(&cl_data->cdev);
+ unregister_chrdev_region(hsc_dev, HSC_DEVS);
+ hsi_client_set_drvdata(cl, NULL);
+ kfree(cl_data);
+
+ return 0;
+}
+
+static struct hsi_client_driver hsc_driver = {
+ .driver = {
+ .name = "hsi_char",
+ .owner = THIS_MODULE,
+ .probe = hsc_probe,
+ .remove = __devexit_p(hsc_remove),
+ },
+};
+
+static int __init hsc_init(void)
+{
+ int ret;
+
+ if ((max_data_size < 4) || (max_data_size > 0x10000) ||
+ (max_data_size & (max_data_size - 1))) {
+ pr_err("Invalid max read/write data size");
+ return -EINVAL;
+ }
+
+ ret = hsi_register_client_driver(&hsc_driver);
+ if (ret) {
+ pr_err("Error while registering HSI/SSI driver %d", ret);
+ return ret;
+ }
+
+ pr_info("HSI/SSI char device loaded\n");
+
+ return 0;
+}
+module_init(hsc_init);
+
+static void __exit hsc_exit(void)
+{
+ hsi_unregister_client_driver(&hsc_driver);
+ pr_info("HSI char device removed\n");
+}
+module_exit(hsc_exit);
+
+MODULE_AUTHOR("Andras Domokos <andras.domokos@nokia.com>");
+MODULE_ALIAS("hsi:hsi_char");
+MODULE_DESCRIPTION("HSI character device");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c
new file mode 100644
index 0000000..4e2d79b
--- /dev/null
+++ b/drivers/hsi/hsi.c
@@ -0,0 +1,494 @@
+/*
+ * HSI core.
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/hsi/hsi.h>
+#include <linux/compiler.h>
+#include <linux/rwsem.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "hsi_core.h"
+
+static struct device_type hsi_ctrl = {
+ .name = "hsi_controller",
+};
+
+static struct device_type hsi_cl = {
+ .name = "hsi_client",
+};
+
+static struct device_type hsi_port = {
+ .name = "hsi_port",
+};
+
+static ssize_t modalias_show(struct device *dev,
+ struct device_attribute *a __maybe_unused, char *buf)
+{
+ return sprintf(buf, "hsi:%s\n", dev_name(dev));
+}
+
+static struct device_attribute hsi_bus_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
+static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ if (dev->type == &hsi_cl)
+ add_uevent_var(env, "MODALIAS=hsi:%s", dev_name(dev));
+
+ return 0;
+}
+
+static int hsi_bus_match(struct device *dev, struct device_driver *driver)
+{
+ return strcmp(dev_name(dev), driver->name) == 0;
+}
+
+static struct bus_type hsi_bus_type = {
+ .name = "hsi",
+ .dev_attrs = hsi_bus_dev_attrs,
+ .match = hsi_bus_match,
+ .uevent = hsi_bus_uevent,
+};
+
+static void hsi_client_release(struct device *dev)
+{
+ kfree(to_hsi_client(dev));
+}
+
+static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info)
+{
+ struct hsi_client *cl;
+ unsigned long flags;
+
+ cl = kzalloc(sizeof(*cl), GFP_KERNEL);
+ if (!cl)
+ return;
+ cl->device.type = &hsi_cl;
+ cl->tx_cfg = info->tx_cfg;
+ cl->rx_cfg = info->rx_cfg;
+ cl->device.bus = &hsi_bus_type;
+ cl->device.parent = &port->device;
+ cl->device.release = hsi_client_release;
+ dev_set_name(&cl->device, info->name);
+ cl->device.platform_data = info->platform_data;
+ spin_lock_irqsave(&port->clock, flags);
+ list_add_tail(&cl->link, &port->clients);
+ spin_unlock_irqrestore(&port->clock, flags);
+ if (info->archdata)
+ cl->device.archdata = *info->archdata;
+ if (device_register(&cl->device) < 0) {
+ pr_err("hsi: failed to register client: %s\n", info->name);
+ kfree(cl);
+ }
+}
+
+static void hsi_scan_board_info(struct hsi_controller *hsi)
+{
+ struct hsi_cl_info *cl_info;
+ struct hsi_port *p;
+
+ list_for_each_entry(cl_info, &hsi_board_list, list)
+ if (cl_info->info.hsi_id == hsi->id) {
+ p = hsi_find_port_num(hsi, cl_info->info.port);
+ if (!p)
+ continue;
+ hsi_new_client(p, &cl_info->info);
+ }
+}
+
+static int hsi_remove_client(struct device *dev, void *data __maybe_unused)
+{
+ struct hsi_client *cl = to_hsi_client(dev);
+ struct hsi_port *port = to_hsi_port(dev->parent);
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->clock, flags);
+ list_del(&cl->link);
+ spin_unlock_irqrestore(&port->clock, flags);
+ device_unregister(dev);
+
+ return 0;
+}
+
+static int hsi_remove_port(struct device *dev, void *data __maybe_unused)
+{
+ device_for_each_child(dev, NULL, hsi_remove_client);
+ device_unregister(dev);
+
+ return 0;
+}
+
+static void hsi_controller_release(struct device *dev __maybe_unused)
+{
+}
+
+static void hsi_port_release(struct device *dev __maybe_unused)
+{
+}
+
+/**
+ * hsi_unregister_controller - Unregister an HSI controller
+ * @hsi: The HSI controller to register
+ */
+void hsi_unregister_controller(struct hsi_controller *hsi)
+{
+ device_for_each_child(&hsi->device, NULL, hsi_remove_port);
+ device_unregister(&hsi->device);
+}
+EXPORT_SYMBOL_GPL(hsi_unregister_controller);
+
+/**
+ * hsi_register_controller - Register an HSI controller and its ports
+ * @hsi: The HSI controller to register
+ *
+ * Returns -errno on failure, 0 on success.
+ */
+int hsi_register_controller(struct hsi_controller *hsi)
+{
+ unsigned int i;
+ int err;
+
+ hsi->device.type = &hsi_ctrl;
+ hsi->device.bus = &hsi_bus_type;
+ hsi->device.release = hsi_controller_release;
+ err = device_register(&hsi->device);
+ if (err < 0)
+ return err;
+ for (i = 0; i < hsi->num_ports; i++) {
+ hsi->port[i].device.parent = &hsi->device;
+ hsi->port[i].device.bus = &hsi_bus_type;
+ hsi->port[i].device.release = hsi_port_release;
+ hsi->port[i].device.type = &hsi_port;
+ INIT_LIST_HEAD(&hsi->port[i].clients);
+ spin_lock_init(&hsi->port[i].clock);
+ err = device_register(&hsi->port[i].device);
+ if (err < 0)
+ goto out;
+ }
+ /* Populate HSI bus with HSI clients */
+ hsi_scan_board_info(hsi);
+
+ return 0;
+out:
+ hsi_unregister_controller(hsi);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(hsi_register_controller);
+
+/**
+ * hsi_register_client_driver - Register an HSI client to the HSI bus
+ * @drv: HSI client driver to register
+ *
+ * Returns -errno on failure, 0 on success.
+ */
+int hsi_register_client_driver(struct hsi_client_driver *drv)
+{
+ drv->driver.bus = &hsi_bus_type;
+
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(hsi_register_client_driver);
+
+static inline int hsi_dummy_msg(struct hsi_msg *msg __maybe_unused)
+{
+ return 0;
+}
+
+static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused)
+{
+ return 0;
+}
+
+/**
+ * hsi_alloc_controller - Allocate an HSI controller and its ports
+ * @n_ports: Number of ports on the HSI controller
+ * @flags: Kernel allocation flags
+ *
+ * Return NULL on failure or a pointer to an hsi_controller on success.
+ */
+struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags)
+{
+ struct hsi_controller *hsi;
+ struct hsi_port *port;
+ unsigned int i;
+
+ if (!n_ports)
+ return NULL;
+
+ port = kzalloc(sizeof(*port)*n_ports, flags);
+ if (!port)
+ return NULL;
+ hsi = kzalloc(sizeof(*hsi), flags);
+ if (!hsi)
+ goto out;
+ for (i = 0; i < n_ports; i++) {
+ dev_set_name(&port[i].device, "port%d", i);
+ port[i].num = i;
+ port[i].async = hsi_dummy_msg;
+ port[i].setup = hsi_dummy_cl;
+ port[i].flush = hsi_dummy_cl;
+ port[i].start_tx = hsi_dummy_cl;
+ port[i].stop_tx = hsi_dummy_cl;
+ port[i].release = hsi_dummy_cl;
+ mutex_init(&port[i].lock);
+ }
+ hsi->num_ports = n_ports;
+ hsi->port = port;
+
+ return hsi;
+out:
+ kfree(port);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(hsi_alloc_controller);
+
+/**
+ * hsi_free_controller - Free an HSI controller
+ * @hsi: Pointer to HSI controller
+ */
+void hsi_free_controller(struct hsi_controller *hsi)
+{
+ if (!hsi)
+ return;
+
+ kfree(hsi->port);
+ kfree(hsi);
+}
+EXPORT_SYMBOL_GPL(hsi_free_controller);
+
+/**
+ * hsi_free_msg - Free an HSI message
+ * @msg: Pointer to the HSI message
+ *
+ * Client is responsible to free the buffers pointed by the scatterlists.
+ */
+void hsi_free_msg(struct hsi_msg *msg)
+{
+ if (!msg)
+ return;
+ sg_free_table(&msg->sgt);
+ kfree(msg);
+}
+EXPORT_SYMBOL_GPL(hsi_free_msg);
+
+/**
+ * hsi_alloc_msg - Allocate an HSI message
+ * @nents: Number of memory entries
+ * @flags: Kernel allocation flags
+ *
+ * nents can be 0. This mainly makes sense for read transfer.
+ * In that case, HSI drivers will call the complete callback when
+ * there is data to be read without consuming it.
+ *
+ * Return NULL on failure or a pointer to an hsi_msg on success.
+ */
+struct hsi_msg *hsi_alloc_msg(unsigned int nents, gfp_t flags)
+{
+ struct hsi_msg *msg;
+ int err;
+
+ msg = kzalloc(sizeof(*msg), flags);
+ if (!msg)
+ return NULL;
+
+ if (!nents)
+ return msg;
+
+ err = sg_alloc_table(&msg->sgt, nents, flags);
+ if (unlikely(err)) {
+ kfree(msg);
+ msg = NULL;
+ }
+
+ return msg;
+}
+EXPORT_SYMBOL_GPL(hsi_alloc_msg);
+
+/**
+ * hsi_async - Submit an HSI transfer to the controller
+ * @cl: HSI client sending the transfer
+ * @msg: The HSI transfer passed to controller
+ *
+ * The HSI message must have the channel, ttype, complete and destructor
+ * fields set beforehand. If nents > 0 then the client has to initialize
+ * also the scatterlists to point to the buffers to write to or read from.
+ *
+ * HSI controllers relay on pre-allocated buffers from their clients and they
+ * do not allocate buffers on their own.
+ *
+ * Once the HSI message transfer finishes, the HSI controller calls the
+ * complete callback with the status and actual_len fields of the HSI message
+ * updated. The complete callback can be called before returning from
+ * hsi_async.
+ *
+ * Returns -errno on failure or 0 on success
+ */
+int hsi_async(struct hsi_client *cl, struct hsi_msg *msg)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+
+ if (!hsi_port_claimed(cl))
+ return -EACCES;
+
+ WARN_ON_ONCE(!msg->destructor || !msg->complete);
+ msg->cl = cl;
+
+ return port->async(msg);
+}
+EXPORT_SYMBOL_GPL(hsi_async);
+
+/**
+ * hsi_claim_port - Claim the HSI client's port
+ * @cl: HSI client that wants to claim its port
+ * @share: Flag to indicate if the client wants to share the port or not.
+ *
+ * Returns -errno on failure, 0 on success.
+ */
+int hsi_claim_port(struct hsi_client *cl, unsigned int share)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+ int err = 0;
+
+ mutex_lock(&port->lock);
+ if ((port->claimed) && (!port->shared || !share)) {
+ err = -EBUSY;
+ goto out;
+ }
+ if (!try_module_get(to_hsi_controller(port->device.parent)->owner)) {
+ err = -ENODEV;
+ goto out;
+ }
+ port->claimed++;
+ port->shared = !!share;
+ cl->pclaimed = 1;
+out:
+ mutex_unlock(&port->lock);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(hsi_claim_port);
+
+/**
+ * hsi_release_port - Release the HSI client's port
+ * @cl: HSI client which previously claimed its port
+ */
+void hsi_release_port(struct hsi_client *cl)
+{
+ struct hsi_port *port = hsi_get_port(cl);
+
+ mutex_lock(&port->lock);
+ /* Allow HW driver to do some cleanup */
+ port->release(cl);
+ if (cl->pclaimed)
+ port->claimed--;
+ BUG_ON(port->claimed < 0);
+ cl->pclaimed = 0;
+ if (!port->claimed)
+ port->shared = 0;
+ module_put(to_hsi_controller(port->device.parent)->owner);
+ mutex_unlock(&port->lock);
+}
+EXPORT_SYMBOL_GPL(hsi_release_port);
+
+static int hsi_start_rx(struct hsi_client *cl, void *data __maybe_unused)
+{
+ if (cl->hsi_start_rx)
+ (*cl->hsi_start_rx)(cl);
+
+ return 0;
+}
+
+static int hsi_stop_rx(struct hsi_client *cl, void *data __maybe_unused)
+{
+ if (cl->hsi_stop_rx)
+ (*cl->hsi_stop_rx)(cl);
+
+ return 0;
+}
+
+static int hsi_port_for_each_client(struct hsi_port *port, void *data,
+ int (*fn)(struct hsi_client *cl, void *data))
+{
+ struct hsi_client *cl;
+
+ spin_lock(&port->clock);
+ list_for_each_entry(cl, &port->clients, link) {
+ spin_unlock(&port->clock);
+ (*fn)(cl, data);
+ spin_lock(&port->clock);
+ }
+ spin_unlock(&port->clock);
+
+ return 0;
+}
+
+/**
+ * hsi_event -Notifies clients about port events
+ * @port: Port where the event occurred
+ * @event: The event type
+ *
+ * Clients should not be concerned about wake line behavior. However, due
+ * to a race condition in HSI HW protocol, clients need to be notified
+ * about wake line changes, so they can implement a workaround for it.
+ *
+ * Events:
+ * HSI_EVENT_START_RX - Incoming wake line high
+ * HSI_EVENT_STOP_RX - Incoming wake line down
+ */
+void hsi_event(struct hsi_port *port, unsigned int event)
+{
+ int (*fn)(struct hsi_client *cl, void *data);
+
+ switch (event) {
+ case HSI_EVENT_START_RX:
+ fn = hsi_start_rx;
+ break;
+ case HSI_EVENT_STOP_RX:
+ fn = hsi_stop_rx;
+ break;
+ default:
+ return;
+ }
+ hsi_port_for_each_client(port, NULL, fn);
+}
+EXPORT_SYMBOL_GPL(hsi_event);
+
+static int __init hsi_init(void)
+{
+ return bus_register(&hsi_bus_type);
+}
+postcore_initcall(hsi_init);
+
+static void __exit hsi_exit(void)
+{
+ bus_unregister(&hsi_bus_type);
+}
+module_exit(hsi_exit);
+
+MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>");
+MODULE_DESCRIPTION("High-speed Synchronous Serial Interface (HSI) framework");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hsi/hsi_boardinfo.c b/drivers/hsi/hsi_boardinfo.c
new file mode 100644
index 0000000..e56bc6d
--- /dev/null
+++ b/drivers/hsi/hsi_boardinfo.c
@@ -0,0 +1,62 @@
+/*
+ * HSI clients registration interface
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/hsi/hsi.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include "hsi_core.h"
+
+/*
+ * hsi_board_list is only used internally by the HSI framework.
+ * No one else is allowed to make use of it.
+ */
+LIST_HEAD(hsi_board_list);
+EXPORT_SYMBOL_GPL(hsi_board_list);
+
+/**
+ * hsi_register_board_info - Register HSI clients information
+ * @info: Array of HSI clients on the board
+ * @len: Length of the array
+ *
+ * HSI clients are statically declared and registered on board files.
+ *
+ * HSI clients will be automatically registered to the HSI bus once the
+ * controller and the port where the clients wishes to attach are registered
+ * to it.
+ *
+ * Return -errno on failure, 0 on success.
+ */
+int __init hsi_register_board_info(struct hsi_board_info const *info,
+ unsigned int len)
+{
+ struct hsi_cl_info *cl_info;
+
+ cl_info = kzalloc(sizeof(*cl_info) * len, GFP_KERNEL);
+ if (!cl_info)
+ return -ENOMEM;
+
+ for (; len; len--, info++, cl_info++) {
+ cl_info->info = *info;
+ list_add_tail(&cl_info->list, &hsi_board_list);
+ }
+
+ return 0;
+}
diff --git a/drivers/hsi/hsi_core.h b/drivers/hsi/hsi_core.h
new file mode 100644
index 0000000..ab5c2fb
--- /dev/null
+++ b/drivers/hsi/hsi_core.h
@@ -0,0 +1,35 @@
+/*
+ * HSI framework internal interfaces,
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Carlos Chinea <carlos.chinea@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __LINUX_HSI_CORE_H__
+#define __LINUX_HSI_CORE_H__
+
+#include <linux/hsi/hsi.h>
+
+struct hsi_cl_info {
+ struct list_head list;
+ struct hsi_board_info info;
+};
+
+extern struct list_head hsi_board_list;
+
+#endif /* __LINUX_HSI_CORE_H__ */
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c
index 8305d29..519ce8b 100644
--- a/drivers/hwmon/fschmd.c
+++ b/drivers/hwmon/fschmd.c
@@ -53,8 +53,8 @@
static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
/* Insmod parameters */
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
index 6c6b240..ce86c5e 100644
--- a/drivers/hwmon/mc13783-adc.c
+++ b/drivers/hwmon/mc13783-adc.c
@@ -59,7 +59,7 @@ static int mc13783_adc_read(struct device *dev,
ret = mc13xxx_adc_do_conversion(priv->mc13xxx,
MC13XXX_ADC_MODE_MULT_CHAN,
- channel, sample);
+ channel, 0, 0, sample);
if (ret)
return ret;
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c
index 834e49d..d6b0bdd 100644
--- a/drivers/hwmon/w83793.c
+++ b/drivers/hwmon/w83793.c
@@ -71,8 +71,8 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in minutes. 2<= timeout <=255 (default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index acba1c6..7f0b832 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -15,7 +15,8 @@
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.
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
* ------------------------------------------------------------------------- */
/* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
@@ -111,7 +112,7 @@ static int sclhi(struct i2c_algo_bit_data *adap)
break;
return -ETIMEDOUT;
}
- cond_resched();
+ cpu_relax();
}
#ifdef DEBUG
if (jiffies != start && i2c_debug >= 3)
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index beb9ffe..73133b1 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -15,7 +15,8 @@
*
* 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.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
*/
#include <linux/kernel.h>
diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c
index 5eebf56..5c23795 100644
--- a/drivers/i2c/algos/i2c-algo-pcf.c
+++ b/drivers/i2c/algos/i2c-algo-pcf.c
@@ -16,7 +16,8 @@
*
* 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.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
*
* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
* Frodo Looijaard <frodol@dds.nl>, and also from Martin Bailey
diff --git a/drivers/i2c/algos/i2c-algo-pcf.h b/drivers/i2c/algos/i2c-algo-pcf.h
index 5263a9e..1ec703e 100644
--- a/drivers/i2c/algos/i2c-algo-pcf.h
+++ b/drivers/i2c/algos/i2c-algo-pcf.h
@@ -16,7 +16,8 @@
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. */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA. */
/* -------------------------------------------------------------------- */
/* With some changes from Frodo Looijaard <frodol@dds.nl> */
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 71c1b0a..d2c5095 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -103,6 +103,7 @@ config I2C_I801
Patsburg (PCH)
DH89xxCC (PCH)
Panther Point (PCH)
+ Lynx Point (PCH)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
diff --git a/drivers/i2c/busses/i2c-acorn.c b/drivers/i2c/busses/i2c-acorn.c
index 8679648..ed9f48d 100644
--- a/drivers/i2c/busses/i2c-acorn.c
+++ b/drivers/i2c/busses/i2c-acorn.c
@@ -19,7 +19,6 @@
#include <mach/hardware.h>
#include <asm/hardware/ioc.h>
-#include <asm/system.h>
#define FORCE_ONES 0xdc
#define SCL 0x02
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index a651779..c0330a4 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -14,8 +14,15 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
-
-#include <asm/gpio.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_i2c.h>
+
+struct i2c_gpio_private_data {
+ struct i2c_adapter adap;
+ struct i2c_algo_bit_data bit_data;
+ struct i2c_gpio_platform_data pdata;
+};
/* Toggle SDA by changing the direction of the pin */
static void i2c_gpio_setsda_dir(void *data, int state)
@@ -78,24 +85,62 @@ static int i2c_gpio_getscl(void *data)
return gpio_get_value(pdata->scl_pin);
}
+static int __devinit of_i2c_gpio_probe(struct device_node *np,
+ struct i2c_gpio_platform_data *pdata)
+{
+ u32 reg;
+
+ if (of_gpio_count(np) < 2)
+ return -ENODEV;
+
+ pdata->sda_pin = of_get_gpio(np, 0);
+ pdata->scl_pin = of_get_gpio(np, 1);
+
+ if (!gpio_is_valid(pdata->sda_pin) || !gpio_is_valid(pdata->scl_pin)) {
+ pr_err("%s: invalid GPIO pins, sda=%d/scl=%d\n",
+ np->full_name, pdata->sda_pin, pdata->scl_pin);
+ return -ENODEV;
+ }
+
+ of_property_read_u32(np, "i2c-gpio,delay-us", &pdata->udelay);
+
+ if (!of_property_read_u32(np, "i2c-gpio,timeout-ms", &reg))
+ pdata->timeout = msecs_to_jiffies(reg);
+
+ pdata->sda_is_open_drain =
+ of_property_read_bool(np, "i2c-gpio,sda-open-drain");
+ pdata->scl_is_open_drain =
+ of_property_read_bool(np, "i2c-gpio,scl-open-drain");
+ pdata->scl_is_output_only =
+ of_property_read_bool(np, "i2c-gpio,scl-output-only");
+
+ return 0;
+}
+
static int __devinit i2c_gpio_probe(struct platform_device *pdev)
{
+ struct i2c_gpio_private_data *priv;
struct i2c_gpio_platform_data *pdata;
struct i2c_algo_bit_data *bit_data;
struct i2c_adapter *adap;
int ret;
- pdata = pdev->dev.platform_data;
- if (!pdata)
- return -ENXIO;
-
- ret = -ENOMEM;
- adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
- if (!adap)
- goto err_alloc_adap;
- bit_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL);
- if (!bit_data)
- goto err_alloc_bit_data;
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ adap = &priv->adap;
+ bit_data = &priv->bit_data;
+ pdata = &priv->pdata;
+
+ if (pdev->dev.of_node) {
+ ret = of_i2c_gpio_probe(pdev->dev.of_node, pdata);
+ if (ret)
+ return ret;
+ } else {
+ if (!pdev->dev.platform_data)
+ return -ENXIO;
+ memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
+ }
ret = gpio_request(pdata->sda_pin, "sda");
if (ret)
@@ -143,6 +188,7 @@ static int __devinit i2c_gpio_probe(struct platform_device *pdev)
adap->algo_data = bit_data;
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
adap->dev.parent = &pdev->dev;
+ adap->dev.of_node = pdev->dev.of_node;
/*
* If "dev->id" is negative we consider it as zero.
@@ -154,7 +200,9 @@ static int __devinit i2c_gpio_probe(struct platform_device *pdev)
if (ret)
goto err_add_bus;
- platform_set_drvdata(pdev, adap);
+ of_i2c_register_devices(adap);
+
+ platform_set_drvdata(pdev, priv);
dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)\n",
pdata->sda_pin, pdata->scl_pin,
@@ -168,34 +216,40 @@ err_add_bus:
err_request_scl:
gpio_free(pdata->sda_pin);
err_request_sda:
- kfree(bit_data);
-err_alloc_bit_data:
- kfree(adap);
-err_alloc_adap:
return ret;
}
static int __devexit i2c_gpio_remove(struct platform_device *pdev)
{
+ struct i2c_gpio_private_data *priv;
struct i2c_gpio_platform_data *pdata;
struct i2c_adapter *adap;
- adap = platform_get_drvdata(pdev);
- pdata = pdev->dev.platform_data;
+ priv = platform_get_drvdata(pdev);
+ adap = &priv->adap;
+ pdata = &priv->pdata;
i2c_del_adapter(adap);
gpio_free(pdata->scl_pin);
gpio_free(pdata->sda_pin);
- kfree(adap->algo_data);
- kfree(adap);
return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id i2c_gpio_dt_ids[] = {
+ { .compatible = "i2c-gpio", },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);
+#endif
+
static struct platform_driver i2c_gpio_driver = {
.driver = {
.name = "i2c-gpio",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(i2c_gpio_dt_ids),
},
.probe = i2c_gpio_probe,
.remove = __devexit_p(i2c_gpio_remove),
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 5d2e281..ae2945a 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -2,7 +2,7 @@
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
<mdsxyz123@yahoo.com>
- Copyright (C) 2007, 2008 Jean Delvare <khali@linux-fr.org>
+ Copyright (C) 2007 - 2012 Jean Delvare <khali@linux-fr.org>
Copyright (C) 2010 Intel Corporation,
David Woodhouse <dwmw2@infradead.org>
@@ -51,6 +51,7 @@
Patsburg (PCH) IDF 0x1d72 32 hard yes yes yes
DH89xxCC (PCH) 0x2330 32 hard yes yes yes
Panther Point (PCH) 0x1e22 32 hard yes yes yes
+ Lynx Point (PCH) 0x8c22 32 hard yes yes yes
Features supported by this driver:
Software PEC no
@@ -105,7 +106,7 @@
#define SMBHSTCNT_KILL 2
/* Other settings */
-#define MAX_TIMEOUT 100
+#define MAX_RETRIES 400
#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
/* I801 command constants */
@@ -145,6 +146,7 @@
#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22
#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
struct i801_priv {
struct i2c_adapter adapter;
@@ -215,7 +217,7 @@ static int i801_check_post(struct i801_priv *priv, int status, int timeout)
dev_dbg(&priv->pci_dev->dev, "Terminating the current operation\n");
outb_p(inb_p(SMBHSTCNT(priv)) | SMBHSTCNT_KILL,
SMBHSTCNT(priv));
- msleep(1);
+ usleep_range(1000, 2000);
outb_p(inb_p(SMBHSTCNT(priv)) & (~SMBHSTCNT_KILL),
SMBHSTCNT(priv));
@@ -272,11 +274,11 @@ static int i801_transaction(struct i801_priv *priv, int xact)
/* We will always wait for a fraction of a second! */
do {
- msleep(1);
+ usleep_range(250, 500);
status = inb_p(SMBHSTSTS(priv));
- } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT));
+ } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_RETRIES));
- result = i801_check_post(priv, status, timeout > MAX_TIMEOUT);
+ result = i801_check_post(priv, status, timeout > MAX_RETRIES);
if (result < 0)
return result;
@@ -291,12 +293,12 @@ static void i801_wait_hwpec(struct i801_priv *priv)
int status;
do {
- msleep(1);
+ usleep_range(250, 500);
status = inb_p(SMBHSTSTS(priv));
} while ((!(status & SMBHSTSTS_INTR))
- && (timeout++ < MAX_TIMEOUT));
+ && (timeout++ < MAX_RETRIES));
- if (timeout > MAX_TIMEOUT)
+ if (timeout > MAX_RETRIES)
dev_dbg(&priv->pci_dev->dev, "PEC Timeout!\n");
outb_p(status, SMBHSTSTS(priv));
@@ -380,12 +382,12 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
/* We will always wait for a fraction of a second! */
timeout = 0;
do {
- msleep(1);
+ usleep_range(250, 500);
status = inb_p(SMBHSTSTS(priv));
} while ((!(status & SMBHSTSTS_BYTE_DONE))
- && (timeout++ < MAX_TIMEOUT));
+ && (timeout++ < MAX_RETRIES));
- result = i801_check_post(priv, status, timeout > MAX_TIMEOUT);
+ result = i801_check_post(priv, status, timeout > MAX_RETRIES);
if (result < 0)
return result;
@@ -633,6 +635,7 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c
index 6561d27..f90a605 100644
--- a/drivers/i2c/busses/i2c-isch.c
+++ b/drivers/i2c/busses/i2c-isch.c
@@ -47,7 +47,7 @@
#define SMBBLKDAT (0x20 + sch_smba)
/* Other settings */
-#define MAX_TIMEOUT 500
+#define MAX_RETRIES 5000
/* I2C constants */
#define SCH_QUICK 0x00
@@ -68,7 +68,7 @@ static int sch_transaction(void)
{
int temp;
int result = 0;
- int timeout = 0;
+ int retries = 0;
dev_dbg(&sch_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT),
@@ -100,12 +100,12 @@ static int sch_transaction(void)
outb(inb(SMBHSTCNT) | 0x10, SMBHSTCNT);
do {
- msleep(1);
+ usleep_range(100, 200);
temp = inb(SMBHSTSTS) & 0x0f;
- } while ((temp & 0x08) && (timeout++ < MAX_TIMEOUT));
+ } while ((temp & 0x08) && (retries++ < MAX_RETRIES));
/* If the SMBus is still busy, we give up */
- if (timeout > MAX_TIMEOUT) {
+ if (retries > MAX_RETRIES) {
dev_err(&sch_adapter.dev, "SMBus Timeout!\n");
result = -ETIMEDOUT;
}
diff --git a/drivers/i2c/i2c-boardinfo.c b/drivers/i2c/i2c-boardinfo.c
index 10274ff..f24cc64 100644
--- a/drivers/i2c/i2c-boardinfo.c
+++ b/drivers/i2c/i2c-boardinfo.c
@@ -13,7 +13,8 @@
*
* 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.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
*/
#include <linux/kernel.h>
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index e9c1893..feb7dc3 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -14,7 +14,8 @@
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. */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA. */
/* ------------------------------------------------------------------------- */
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>.
diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h
index 9f9c57f..18a8fd2 100644
--- a/drivers/i2c/i2c-core.h
+++ b/drivers/i2c/i2c-core.h
@@ -13,7 +13,8 @@
*
* 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.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
*/
#include <linux/rwsem.h>
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index 10e7f1e..4504832 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -17,7 +17,8 @@
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.
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
*/
/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module.
diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
index f61ccc1..9836d08 100644
--- a/drivers/i2c/i2c-smbus.c
+++ b/drivers/i2c/i2c-smbus.c
@@ -16,7 +16,8 @@
*
* 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.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA.
*/
#include <linux/kernel.h>
diff --git a/drivers/i2c/muxes/pca9541.c b/drivers/i2c/muxes/pca9541.c
index ed699c5..e0df9b6 100644
--- a/drivers/i2c/muxes/pca9541.c
+++ b/drivers/i2c/muxes/pca9541.c
@@ -393,18 +393,7 @@ static struct i2c_driver pca9541_driver = {
.id_table = pca9541_id,
};
-static int __init pca9541_init(void)
-{
- return i2c_add_driver(&pca9541_driver);
-}
-
-static void __exit pca9541_exit(void)
-{
- i2c_del_driver(&pca9541_driver);
-}
-
-module_init(pca9541_init);
-module_exit(pca9541_exit);
+module_i2c_driver(pca9541_driver);
MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
MODULE_DESCRIPTION("PCA9541 I2C master selector driver");
diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c
index 6f89536..0e37ef2 100644
--- a/drivers/i2c/muxes/pca954x.c
+++ b/drivers/i2c/muxes/pca954x.c
@@ -284,18 +284,7 @@ static struct i2c_driver pca954x_driver = {
.id_table = pca954x_id,
};
-static int __init pca954x_init(void)
-{
- return i2c_add_driver(&pca954x_driver);
-}
-
-static void __exit pca954x_exit(void)
-{
- i2c_del_driver(&pca954x_driver);
-}
-
-module_init(pca954x_init);
-module_exit(pca954x_exit);
+module_i2c_driver(pca954x_driver);
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
MODULE_DESCRIPTION("PCA954x I2C mux/switch driver");
diff --git a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c
index d2f3db3..28e344e 100644
--- a/drivers/ide/ide-cs.c
+++ b/drivers/ide/ide-cs.c
@@ -41,7 +41,6 @@
#include <linux/major.h>
#include <linux/delay.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
diff --git a/drivers/ide/qd65xx.c b/drivers/ide/qd65xx.c
index 8bbfe55..e03f4f1 100644
--- a/drivers/ide/qd65xx.c
+++ b/drivers/ide/qd65xx.c
@@ -29,7 +29,6 @@
#include <linux/blkdev.h>
#include <linux/ide.h>
#include <linux/init.h>
-#include <asm/system.h>
#include <asm/io.h>
#define DRV_NAME "qd65xx"
diff --git a/drivers/infiniband/hw/ehca/ehca_reqs.c b/drivers/infiniband/hw/ehca/ehca_reqs.c
index 9a3fbfc..fd05f48 100644
--- a/drivers/infiniband/hw/ehca/ehca_reqs.c
+++ b/drivers/infiniband/hw/ehca/ehca_reqs.c
@@ -42,7 +42,6 @@
*/
-#include <asm/system.h>
#include "ehca_classes.h"
#include "ehca_tools.h"
#include "ehca_qes.h"
diff --git a/drivers/input/input-compat.c b/drivers/input/input-compat.c
index e46a867..64ca711 100644
--- a/drivers/input/input-compat.c
+++ b/drivers/input/input-compat.c
@@ -17,7 +17,7 @@
int input_event_from_user(const char __user *buffer,
struct input_event *event)
{
- if (INPUT_COMPAT_TEST) {
+ if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
struct input_event_compat compat_event;
if (copy_from_user(&compat_event, buffer,
@@ -41,7 +41,7 @@ int input_event_from_user(const char __user *buffer,
int input_event_to_user(char __user *buffer,
const struct input_event *event)
{
- if (INPUT_COMPAT_TEST) {
+ if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
struct input_event_compat compat_event;
compat_event.time.tv_sec = event->time.tv_sec;
diff --git a/drivers/input/input-compat.h b/drivers/input/input-compat.h
index 22be27b..148f66f 100644
--- a/drivers/input/input-compat.h
+++ b/drivers/input/input-compat.h
@@ -67,7 +67,7 @@ struct ff_effect_compat {
static inline size_t input_event_size(void)
{
- return INPUT_COMPAT_TEST ?
+ return (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) ?
sizeof(struct input_event_compat) : sizeof(struct input_event);
}
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index c24ec2d..26043cc 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -13,7 +13,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <asm/io.h>
-#include <asm/system.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/joystick.h>
diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c
index 0bc8620..c65b5fa 100644
--- a/drivers/input/joystick/amijoy.c
+++ b/drivers/input/joystick/amijoy.c
@@ -35,7 +35,6 @@
#include <linux/interrupt.h>
#include <linux/mutex.h>
-#include <asm/system.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
@@ -108,6 +107,9 @@ static int __init amijoy_init(void)
int i, j;
int err;
+ if (!MACH_IS_AMIGA)
+ return -ENODEV;
+
for (i = 0; i < 2; i++) {
if (!amijoy[i])
continue;
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index ed1ed46..62bfce4 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -28,14 +28,18 @@
#include <linux/gpio.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
+#include <linux/spinlock.h>
struct gpio_button_data {
- struct gpio_keys_button *button;
+ const struct gpio_keys_button *button;
struct input_dev *input;
struct timer_list timer;
struct work_struct work;
- int timer_debounce; /* in msecs */
+ unsigned int timer_debounce; /* in msecs */
+ unsigned int irq;
+ spinlock_t lock;
bool disabled;
+ bool key_pressed;
};
struct gpio_keys_drvdata {
@@ -114,7 +118,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
/*
* Disable IRQ and possible debouncing timer.
*/
- disable_irq(gpio_to_irq(bdata->button->gpio));
+ disable_irq(bdata->irq);
if (bdata->timer_debounce)
del_timer_sync(&bdata->timer);
@@ -135,7 +139,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
static void gpio_keys_enable_button(struct gpio_button_data *bdata)
{
if (bdata->disabled) {
- enable_irq(gpio_to_irq(bdata->button->gpio));
+ enable_irq(bdata->irq);
bdata->disabled = false;
}
}
@@ -195,7 +199,7 @@ static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata,
* @type: button type (%EV_KEY, %EV_SW)
*
* This function parses stringified bitmap from @buf and disables/enables
- * GPIO buttons accordinly. Returns 0 on success and negative error
+ * GPIO buttons accordingly. Returns 0 on success and negative error
* on failure.
*/
static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
@@ -320,9 +324,9 @@ static struct attribute_group gpio_keys_attr_group = {
.attrs = gpio_keys_attrs,
};
-static void gpio_keys_report_event(struct gpio_button_data *bdata)
+static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
{
- struct gpio_keys_button *button = bdata->button;
+ const struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;
@@ -336,27 +340,26 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata)
input_sync(input);
}
-static void gpio_keys_work_func(struct work_struct *work)
+static void gpio_keys_gpio_work_func(struct work_struct *work)
{
struct gpio_button_data *bdata =
container_of(work, struct gpio_button_data, work);
- gpio_keys_report_event(bdata);
+ gpio_keys_gpio_report_event(bdata);
}
-static void gpio_keys_timer(unsigned long _data)
+static void gpio_keys_gpio_timer(unsigned long _data)
{
- struct gpio_button_data *data = (struct gpio_button_data *)_data;
+ struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
- schedule_work(&data->work);
+ schedule_work(&bdata->work);
}
-static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
+static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata = dev_id;
- struct gpio_keys_button *button = bdata->button;
- BUG_ON(irq != gpio_to_irq(button->gpio));
+ BUG_ON(irq != bdata->irq);
if (bdata->timer_debounce)
mod_timer(&bdata->timer,
@@ -367,50 +370,133 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static void gpio_keys_irq_timer(unsigned long _data)
+{
+ struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
+ struct input_dev *input = bdata->input;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bdata->lock, flags);
+ if (bdata->key_pressed) {
+ input_event(input, EV_KEY, bdata->button->code, 0);
+ input_sync(input);
+ bdata->key_pressed = false;
+ }
+ spin_unlock_irqrestore(&bdata->lock, flags);
+}
+
+static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
+{
+ struct gpio_button_data *bdata = dev_id;
+ const struct gpio_keys_button *button = bdata->button;
+ struct input_dev *input = bdata->input;
+ unsigned long flags;
+
+ BUG_ON(irq != bdata->irq);
+
+ spin_lock_irqsave(&bdata->lock, flags);
+
+ if (!bdata->key_pressed) {
+ input_event(input, EV_KEY, button->code, 1);
+ input_sync(input);
+
+ if (!bdata->timer_debounce) {
+ input_event(input, EV_KEY, button->code, 0);
+ input_sync(input);
+ goto out;
+ }
+
+ bdata->key_pressed = true;
+ }
+
+ if (bdata->timer_debounce)
+ mod_timer(&bdata->timer,
+ jiffies + msecs_to_jiffies(bdata->timer_debounce));
+out:
+ spin_unlock_irqrestore(&bdata->lock, flags);
+ return IRQ_HANDLED;
+}
+
static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
+ struct input_dev *input,
struct gpio_button_data *bdata,
- struct gpio_keys_button *button)
+ const struct gpio_keys_button *button)
{
const char *desc = button->desc ? button->desc : "gpio_keys";
struct device *dev = &pdev->dev;
+ irq_handler_t isr;
unsigned long irqflags;
int irq, error;
- setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata);
- INIT_WORK(&bdata->work, gpio_keys_work_func);
+ bdata->input = input;
+ bdata->button = button;
+ spin_lock_init(&bdata->lock);
- error = gpio_request(button->gpio, desc);
- if (error < 0) {
- dev_err(dev, "failed to request GPIO %d, error %d\n",
- button->gpio, error);
- goto fail2;
- }
+ if (gpio_is_valid(button->gpio)) {
- error = gpio_direction_input(button->gpio);
- if (error < 0) {
- dev_err(dev, "failed to configure"
- " direction for GPIO %d, error %d\n",
- button->gpio, error);
- goto fail3;
- }
+ error = gpio_request(button->gpio, desc);
+ if (error < 0) {
+ dev_err(dev, "Failed to request GPIO %d, error %d\n",
+ button->gpio, error);
+ return error;
+ }
- if (button->debounce_interval) {
- error = gpio_set_debounce(button->gpio,
- button->debounce_interval * 1000);
- /* use timer if gpiolib doesn't provide debounce */
- if (error < 0)
- bdata->timer_debounce = button->debounce_interval;
- }
+ error = gpio_direction_input(button->gpio);
+ if (error < 0) {
+ dev_err(dev,
+ "Failed to configure direction for GPIO %d, error %d\n",
+ button->gpio, error);
+ goto fail;
+ }
- irq = gpio_to_irq(button->gpio);
- if (irq < 0) {
- error = irq;
- dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n",
- button->gpio, error);
- goto fail3;
+ if (button->debounce_interval) {
+ error = gpio_set_debounce(button->gpio,
+ button->debounce_interval * 1000);
+ /* use timer if gpiolib doesn't provide debounce */
+ if (error < 0)
+ bdata->timer_debounce =
+ button->debounce_interval;
+ }
+
+ irq = gpio_to_irq(button->gpio);
+ if (irq < 0) {
+ error = irq;
+ dev_err(dev,
+ "Unable to get irq number for GPIO %d, error %d\n",
+ button->gpio, error);
+ goto fail;
+ }
+ bdata->irq = irq;
+
+ INIT_WORK(&bdata->work, gpio_keys_gpio_work_func);
+ setup_timer(&bdata->timer,
+ gpio_keys_gpio_timer, (unsigned long)bdata);
+
+ isr = gpio_keys_gpio_isr;
+ irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+ } else {
+ if (!button->irq) {
+ dev_err(dev, "No IRQ specified\n");
+ return -EINVAL;
+ }
+ bdata->irq = button->irq;
+
+ if (button->type && button->type != EV_KEY) {
+ dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n");
+ return -EINVAL;
+ }
+
+ bdata->timer_debounce = button->debounce_interval;
+ setup_timer(&bdata->timer,
+ gpio_keys_irq_timer, (unsigned long)bdata);
+
+ isr = gpio_keys_irq_isr;
+ irqflags = 0;
}
- irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+ input_set_capability(input, button->type ?: EV_KEY, button->code);
+
/*
* If platform has specified that the button can be disabled,
* we don't want it to share the interrupt line.
@@ -418,18 +504,19 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
if (!button->can_disable)
irqflags |= IRQF_SHARED;
- error = request_threaded_irq(irq, NULL, gpio_keys_isr, irqflags, desc, bdata);
+ error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata);
if (error < 0) {
dev_err(dev, "Unable to claim irq %d; error %d\n",
- irq, error);
- goto fail3;
+ bdata->irq, error);
+ goto fail;
}
return 0;
-fail3:
- gpio_free(button->gpio);
-fail2:
+fail:
+ if (gpio_is_valid(button->gpio))
+ gpio_free(button->gpio);
+
return error;
}
@@ -547,9 +634,19 @@ static int gpio_keys_get_devtree_pdata(struct device *dev,
#endif
+static void gpio_remove_key(struct gpio_button_data *bdata)
+{
+ free_irq(bdata->irq, bdata);
+ if (bdata->timer_debounce)
+ del_timer_sync(&bdata->timer);
+ cancel_work_sync(&bdata->work);
+ if (gpio_is_valid(bdata->button->gpio))
+ gpio_free(bdata->button->gpio);
+}
+
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
- struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+ const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_drvdata *ddata;
struct device *dev = &pdev->dev;
struct gpio_keys_platform_data alt_pdata;
@@ -599,21 +696,15 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < pdata->nbuttons; i++) {
- struct gpio_keys_button *button = &pdata->buttons[i];
+ const struct gpio_keys_button *button = &pdata->buttons[i];
struct gpio_button_data *bdata = &ddata->data[i];
- unsigned int type = button->type ?: EV_KEY;
-
- bdata->input = input;
- bdata->button = button;
- error = gpio_keys_setup_key(pdev, bdata, button);
+ error = gpio_keys_setup_key(pdev, input, bdata, button);
if (error)
goto fail2;
if (button->wakeup)
wakeup = 1;
-
- input_set_capability(input, type, button->code);
}
error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);
@@ -630,9 +721,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
goto fail3;
}
- /* get current state of buttons */
- for (i = 0; i < pdata->nbuttons; i++)
- gpio_keys_report_event(&ddata->data[i]);
+ /* get current state of buttons that are connected to GPIOs */
+ for (i = 0; i < pdata->nbuttons; i++) {
+ struct gpio_button_data *bdata = &ddata->data[i];
+ if (gpio_is_valid(bdata->button->gpio))
+ gpio_keys_gpio_report_event(bdata);
+ }
input_sync(input);
device_init_wakeup(&pdev->dev, wakeup);
@@ -642,13 +736,8 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
fail3:
sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
fail2:
- while (--i >= 0) {
- free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
- if (ddata->data[i].timer_debounce)
- del_timer_sync(&ddata->data[i].timer);
- cancel_work_sync(&ddata->data[i].work);
- gpio_free(pdata->buttons[i].gpio);
- }
+ while (--i >= 0)
+ gpio_remove_key(&ddata->data[i]);
platform_set_drvdata(pdev, NULL);
fail1:
@@ -671,14 +760,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 0);
- for (i = 0; i < ddata->n_buttons; i++) {
- int irq = gpio_to_irq(ddata->data[i].button->gpio);
- free_irq(irq, &ddata->data[i]);
- if (ddata->data[i].timer_debounce)
- del_timer_sync(&ddata->data[i].timer);
- cancel_work_sync(&ddata->data[i].work);
- gpio_free(ddata->data[i].button->gpio);
- }
+ for (i = 0; i < ddata->n_buttons; i++)
+ gpio_remove_key(&ddata->data[i]);
input_unregister_device(input);
@@ -703,11 +786,9 @@ static int gpio_keys_suspend(struct device *dev)
if (device_may_wakeup(dev)) {
for (i = 0; i < ddata->n_buttons; i++) {
- struct gpio_keys_button *button = ddata->data[i].button;
- if (button->wakeup) {
- int irq = gpio_to_irq(button->gpio);
- enable_irq_wake(irq);
- }
+ struct gpio_button_data *bdata = &ddata->data[i];
+ if (bdata->button->wakeup)
+ enable_irq_wake(bdata->irq);
}
}
@@ -720,14 +801,12 @@ static int gpio_keys_resume(struct device *dev)
int i;
for (i = 0; i < ddata->n_buttons; i++) {
+ struct gpio_button_data *bdata = &ddata->data[i];
+ if (bdata->button->wakeup && device_may_wakeup(dev))
+ disable_irq_wake(bdata->irq);
- struct gpio_keys_button *button = ddata->data[i].button;
- if (button->wakeup && device_may_wakeup(dev)) {
- int irq = gpio_to_irq(button->gpio);
- disable_irq_wake(irq);
- }
-
- gpio_keys_report_event(&ddata->data[i]);
+ if (gpio_is_valid(bdata->button->gpio))
+ gpio_keys_gpio_report_event(bdata);
}
input_sync(ddata->input);
diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c
index eeafc30..9d639fa 100644
--- a/drivers/input/keyboard/jornada720_kbd.c
+++ b/drivers/input/keyboard/jornada720_kbd.c
@@ -27,6 +27,7 @@
#include <mach/jornada720.h>
#include <mach/hardware.h>
+#include <mach/irqs.h>
MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver");
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
index 21c42f8..fe4ac95 100644
--- a/drivers/input/keyboard/tegra-kbc.c
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -630,6 +630,7 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev)
if (!np)
return NULL;
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
diff --git a/drivers/input/misc/88pm860x_onkey.c b/drivers/input/misc/88pm860x_onkey.c
index f2e0cbc..f9ce183 100644
--- a/drivers/input/misc/88pm860x_onkey.c
+++ b/drivers/input/misc/88pm860x_onkey.c
@@ -105,6 +105,8 @@ static int __devinit pm860x_onkey_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, info);
+ device_init_wakeup(&pdev->dev, 1);
+
return 0;
out_irq:
@@ -129,10 +131,34 @@ static int __devexit pm860x_onkey_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int pm860x_onkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev))
+ chip->wakeup_flag |= 1 << PM8607_IRQ_ONKEY;
+ return 0;
+}
+static int pm860x_onkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev))
+ chip->wakeup_flag &= ~(1 << PM8607_IRQ_ONKEY);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm860x_onkey_pm_ops, pm860x_onkey_suspend, pm860x_onkey_resume);
+
static struct platform_driver pm860x_onkey_driver = {
.driver = {
.name = "88pm860x-onkey",
.owner = THIS_MODULE,
+ .pm = &pm860x_onkey_pm_ops,
},
.probe = pm860x_onkey_probe,
.remove = __devexit_p(pm860x_onkey_remove),
diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c
index ff5f61a..5fa9934 100644
--- a/drivers/input/mouse/amimouse.c
+++ b/drivers/input/mouse/amimouse.c
@@ -25,7 +25,6 @@
#include <asm/irq.h>
#include <asm/setup.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
diff --git a/drivers/input/mouse/atarimouse.c b/drivers/input/mouse/atarimouse.c
index 5c4a692..d1c4323 100644
--- a/drivers/input/mouse/atarimouse.c
+++ b/drivers/input/mouse/atarimouse.c
@@ -47,7 +47,6 @@
#include <asm/irq.h>
#include <asm/setup.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/atarihw.h>
#include <asm/atarikb.h>
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
index 2a77a52..a977bfa 100644
--- a/drivers/input/mouse/sentelic.c
+++ b/drivers/input/mouse/sentelic.c
@@ -2,7 +2,7 @@
* Finger Sensing Pad PS/2 mouse driver.
*
* Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
- * Copyright (C) 2005-2011 Tai-hwa Liang, Sentelic Corporation.
+ * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/ctype.h>
#include <linux/libps2.h>
#include <linux/serio.h>
@@ -36,6 +37,9 @@
#define FSP_CMD_TIMEOUT 200
#define FSP_CMD_TIMEOUT2 30
+#define GET_ABS_X(packet) ((packet[1] << 2) | ((packet[3] >> 2) & 0x03))
+#define GET_ABS_Y(packet) ((packet[2] << 2) | (packet[3] & 0x03))
+
/** Driver version. */
static const char fsp_drv_ver[] = "1.0.0-K";
@@ -128,8 +132,9 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
out:
ps2_end_command(ps2dev);
psmouse_activate(psmouse);
- dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
- reg_addr, *reg_val, rc);
+ psmouse_dbg(psmouse,
+ "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
+ reg_addr, *reg_val, rc);
return rc;
}
@@ -179,8 +184,9 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)
out:
ps2_end_command(ps2dev);
- dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",
- reg_addr, reg_val, rc);
+ psmouse_dbg(psmouse,
+ "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",
+ reg_addr, reg_val, rc);
return rc;
}
@@ -237,8 +243,9 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
out:
ps2_end_command(ps2dev);
psmouse_activate(psmouse);
- dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n",
- *reg_val, rc);
+ psmouse_dbg(psmouse,
+ "READ PAGE REG: 0x%02x (rc = %d)\n",
+ *reg_val, rc);
return rc;
}
@@ -274,8 +281,9 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)
out:
ps2_end_command(ps2dev);
- dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n",
- reg_val, rc);
+ psmouse_dbg(psmouse,
+ "WRITE PAGE REG: to 0x%02x (rc = %d)\n",
+ reg_val, rc);
return rc;
}
@@ -319,7 +327,7 @@ static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable)
int res = 0;
if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) {
- dev_err(&psmouse->ps2dev.serio->dev, "Unable get OPC state.\n");
+ psmouse_err(psmouse, "Unable get OPC state.\n");
return -EIO;
}
@@ -336,8 +344,7 @@ static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable)
}
if (res != 0) {
- dev_err(&psmouse->ps2dev.serio->dev,
- "Unable to enable OPC tag.\n");
+ psmouse_err(psmouse, "Unable to enable OPC tag.\n");
res = -EIO;
}
@@ -615,18 +622,40 @@ static struct attribute_group fsp_attribute_group = {
.attrs = fsp_attributes,
};
-#ifdef FSP_DEBUG
-static void fsp_packet_debug(unsigned char packet[])
+#ifdef FSP_DEBUG
+static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[])
{
static unsigned int ps2_packet_cnt;
static unsigned int ps2_last_second;
unsigned int jiffies_msec;
+ const char *packet_type = "UNKNOWN";
+ unsigned short abs_x = 0, abs_y = 0;
+
+ /* Interpret & dump the packet data. */
+ switch (packet[0] >> FSP_PKT_TYPE_SHIFT) {
+ case FSP_PKT_TYPE_ABS:
+ packet_type = "Absolute";
+ abs_x = GET_ABS_X(packet);
+ abs_y = GET_ABS_Y(packet);
+ break;
+ case FSP_PKT_TYPE_NORMAL:
+ packet_type = "Normal";
+ break;
+ case FSP_PKT_TYPE_NOTIFY:
+ packet_type = "Notify";
+ break;
+ case FSP_PKT_TYPE_NORMAL_OPC:
+ packet_type = "Normal-OPC";
+ break;
+ }
ps2_packet_cnt++;
jiffies_msec = jiffies_to_msecs(jiffies);
psmouse_dbg(psmouse,
- "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n",
- jiffies_msec, packet[0], packet[1], packet[2], packet[3]);
+ "%08dms %s packets: %02x, %02x, %02x, %02x; "
+ "abs_x: %d, abs_y: %d\n",
+ jiffies_msec, packet_type,
+ packet[0], packet[1], packet[2], packet[3], abs_x, abs_y);
if (jiffies_msec - ps2_last_second > 1000) {
psmouse_dbg(psmouse, "PS/2 packets/sec = %d\n", ps2_packet_cnt);
@@ -635,17 +664,29 @@ static void fsp_packet_debug(unsigned char packet[])
}
}
#else
-static void fsp_packet_debug(unsigned char packet[])
+static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[])
{
}
#endif
+static void fsp_set_slot(struct input_dev *dev, int slot, bool active,
+ unsigned int x, unsigned int y)
+{
+ input_mt_slot(dev, slot);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
+ if (active) {
+ input_report_abs(dev, ABS_MT_POSITION_X, x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, y);
+ }
+}
+
static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
struct fsp_data *ad = psmouse->private;
unsigned char *packet = psmouse->packet;
unsigned char button_status = 0, lscroll = 0, rscroll = 0;
+ unsigned short abs_x, abs_y, fgrs = 0;
int rel_x, rel_y;
if (psmouse->pktcnt < 4)
@@ -655,16 +696,76 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
* Full packet accumulated, process it
*/
+ fsp_packet_debug(psmouse, packet);
+
switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) {
case FSP_PKT_TYPE_ABS:
- dev_warn(&psmouse->ps2dev.serio->dev,
- "Unexpected absolute mode packet, ignored.\n");
+ abs_x = GET_ABS_X(packet);
+ abs_y = GET_ABS_Y(packet);
+
+ if (packet[0] & FSP_PB0_MFMC) {
+ /*
+ * MFMC packet: assume that there are two fingers on
+ * pad
+ */
+ fgrs = 2;
+
+ /* MFMC packet */
+ if (packet[0] & FSP_PB0_MFMC_FGR2) {
+ /* 2nd finger */
+ if (ad->last_mt_fgr == 2) {
+ /*
+ * workaround for buggy firmware
+ * which doesn't clear MFMC bit if
+ * the 1st finger is up
+ */
+ fgrs = 1;
+ fsp_set_slot(dev, 0, false, 0, 0);
+ }
+ ad->last_mt_fgr = 2;
+
+ fsp_set_slot(dev, 1, fgrs == 2, abs_x, abs_y);
+ } else {
+ /* 1st finger */
+ if (ad->last_mt_fgr == 1) {
+ /*
+ * workaround for buggy firmware
+ * which doesn't clear MFMC bit if
+ * the 2nd finger is up
+ */
+ fgrs = 1;
+ fsp_set_slot(dev, 1, false, 0, 0);
+ }
+ ad->last_mt_fgr = 1;
+ fsp_set_slot(dev, 0, fgrs != 0, abs_x, abs_y);
+ }
+ } else {
+ /* SFAC packet */
+
+ /* no multi-finger information */
+ ad->last_mt_fgr = 0;
+
+ if (abs_x != 0 && abs_y != 0)
+ fgrs = 1;
+
+ fsp_set_slot(dev, 0, fgrs > 0, abs_x, abs_y);
+ fsp_set_slot(dev, 1, false, 0, 0);
+ }
+ if (fgrs > 0) {
+ input_report_abs(dev, ABS_X, abs_x);
+ input_report_abs(dev, ABS_Y, abs_y);
+ }
+ input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+ input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+ input_report_key(dev, BTN_TOUCH, fgrs);
+ input_report_key(dev, BTN_TOOL_FINGER, fgrs == 1);
+ input_report_key(dev, BTN_TOOL_DOUBLETAP, fgrs == 2);
break;
case FSP_PKT_TYPE_NORMAL_OPC:
/* on-pad click, filter it if necessary */
if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC)
- packet[0] &= ~BIT(0);
+ packet[0] &= ~FSP_PB0_LBTN;
/* fall through */
case FSP_PKT_TYPE_NORMAL:
@@ -711,8 +812,6 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
input_sync(dev);
- fsp_packet_debug(packet);
-
return PSMOUSE_FULL_PACKET;
}
@@ -736,42 +835,106 @@ static int fsp_activate_protocol(struct psmouse *psmouse)
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 0x04) {
- dev_err(&psmouse->ps2dev.serio->dev,
- "Unable to enable 4 bytes packet format.\n");
+ psmouse_err(psmouse,
+ "Unable to enable 4 bytes packet format.\n");
return -EIO;
}
- if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) {
- dev_err(&psmouse->ps2dev.serio->dev,
- "Unable to read SYSCTL5 register.\n");
- return -EIO;
- }
+ if (pad->ver < FSP_VER_STL3888_C0) {
+ /* Preparing relative coordinates output for older hardware */
+ if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) {
+ psmouse_err(psmouse,
+ "Unable to read SYSCTL5 register.\n");
+ return -EIO;
+ }
- val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8);
- /* Ensure we are not in absolute mode */
- val &= ~FSP_BIT_EN_PKT_G0;
- if (pad->buttons == 0x06) {
- /* Left/Middle/Right & Scroll Up/Down/Right/Left */
- val |= FSP_BIT_EN_MSID6;
- }
+ if (fsp_get_buttons(psmouse, &pad->buttons)) {
+ psmouse_err(psmouse,
+ "Unable to retrieve number of buttons.\n");
+ return -EIO;
+ }
- if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) {
- dev_err(&psmouse->ps2dev.serio->dev,
- "Unable to set up required mode bits.\n");
- return -EIO;
+ val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8);
+ /* Ensure we are not in absolute mode */
+ val &= ~FSP_BIT_EN_PKT_G0;
+ if (pad->buttons == 0x06) {
+ /* Left/Middle/Right & Scroll Up/Down/Right/Left */
+ val |= FSP_BIT_EN_MSID6;
+ }
+
+ if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) {
+ psmouse_err(psmouse,
+ "Unable to set up required mode bits.\n");
+ return -EIO;
+ }
+
+ /*
+ * Enable OPC tags such that driver can tell the difference
+ * between on-pad and real button click
+ */
+ if (fsp_opc_tag_enable(psmouse, true))
+ psmouse_warn(psmouse,
+ "Failed to enable OPC tag mode.\n");
+ /* enable on-pad click by default */
+ pad->flags |= FSPDRV_FLAG_EN_OPC;
+
+ /* Enable on-pad vertical and horizontal scrolling */
+ fsp_onpad_vscr(psmouse, true);
+ fsp_onpad_hscr(psmouse, true);
+ } else {
+ /* Enable absolute coordinates output for Cx/Dx hardware */
+ if (fsp_reg_write(psmouse, FSP_REG_SWC1,
+ FSP_BIT_SWC1_EN_ABS_1F |
+ FSP_BIT_SWC1_EN_ABS_2F |
+ FSP_BIT_SWC1_EN_FUP_OUT |
+ FSP_BIT_SWC1_EN_ABS_CON)) {
+ psmouse_err(psmouse,
+ "Unable to enable absolute coordinates output.\n");
+ return -EIO;
+ }
}
- /*
- * Enable OPC tags such that driver can tell the difference between
- * on-pad and real button click
- */
- if (fsp_opc_tag_enable(psmouse, true))
- dev_warn(&psmouse->ps2dev.serio->dev,
- "Failed to enable OPC tag mode.\n");
+ return 0;
+}
- /* Enable on-pad vertical and horizontal scrolling */
- fsp_onpad_vscr(psmouse, true);
- fsp_onpad_hscr(psmouse, true);
+static int fsp_set_input_params(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct fsp_data *pad = psmouse->private;
+
+ if (pad->ver < FSP_VER_STL3888_C0) {
+ __set_bit(BTN_MIDDLE, dev->keybit);
+ __set_bit(BTN_BACK, dev->keybit);
+ __set_bit(BTN_FORWARD, dev->keybit);
+ __set_bit(REL_WHEEL, dev->relbit);
+ __set_bit(REL_HWHEEL, dev->relbit);
+ } else {
+ /*
+ * Hardware prior to Cx performs much better in relative mode;
+ * hence, only enable absolute coordinates output as well as
+ * multi-touch output for the newer hardware.
+ *
+ * Maximum coordinates can be computed as:
+ *
+ * number of scanlines * 64 - 57
+ *
+ * where number of X/Y scanline lines are 16/12.
+ */
+ int abs_x = 967, abs_y = 711;
+
+ __set_bit(EV_ABS, dev->evbit);
+ __clear_bit(EV_REL, dev->evbit);
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOOL_FINGER, dev->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+ __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
+
+ input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0);
+ input_mt_init_slots(dev, 2);
+ input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0);
+ }
return 0;
}
@@ -829,18 +992,16 @@ static int fsp_reconnect(struct psmouse *psmouse)
int fsp_init(struct psmouse *psmouse)
{
struct fsp_data *priv;
- int ver, rev, buttons;
+ int ver, rev;
int error;
if (fsp_get_version(psmouse, &ver) ||
- fsp_get_revision(psmouse, &rev) ||
- fsp_get_buttons(psmouse, &buttons)) {
+ fsp_get_revision(psmouse, &rev)) {
return -ENODEV;
}
- psmouse_info(psmouse,
- "Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n",
- ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7);
+ psmouse_info(psmouse, "Finger Sensing Pad, hw: %d.%d.%d, sw: %s\n",
+ ver >> 4, ver & 0x0F, rev, fsp_drv_ver);
psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL);
if (!priv)
@@ -848,17 +1009,6 @@ int fsp_init(struct psmouse *psmouse)
priv->ver = ver;
priv->rev = rev;
- priv->buttons = buttons;
-
- /* enable on-pad click by default */
- priv->flags |= FSPDRV_FLAG_EN_OPC;
-
- /* Set up various supported input event bits */
- __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
- __set_bit(BTN_BACK, psmouse->dev->keybit);
- __set_bit(BTN_FORWARD, psmouse->dev->keybit);
- __set_bit(REL_WHEEL, psmouse->dev->relbit);
- __set_bit(REL_HWHEEL, psmouse->dev->relbit);
psmouse->protocol_handler = fsp_process_byte;
psmouse->disconnect = fsp_disconnect;
@@ -866,16 +1016,20 @@ int fsp_init(struct psmouse *psmouse)
psmouse->cleanup = fsp_reset;
psmouse->pktsize = 4;
- /* set default packet output based on number of buttons we found */
error = fsp_activate_protocol(psmouse);
if (error)
goto err_out;
+ /* Set up various supported input event bits */
+ error = fsp_set_input_params(psmouse);
+ if (error)
+ goto err_out;
+
error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
&fsp_attribute_group);
if (error) {
- dev_err(&psmouse->ps2dev.serio->dev,
- "Failed to create sysfs attributes (%d)", error);
+ psmouse_err(psmouse,
+ "Failed to create sysfs attributes (%d)", error);
goto err_out;
}
diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h
index 2e4af24..334de19 100644
--- a/drivers/input/mouse/sentelic.h
+++ b/drivers/input/mouse/sentelic.h
@@ -2,7 +2,7 @@
* Finger Sensing Pad PS/2 mouse driver.
*
* Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
- * Copyright (C) 2005-2011 Tai-hwa Liang, Sentelic Corporation.
+ * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -55,6 +55,16 @@
#define FSP_BIT_FIX_HSCR BIT(5)
#define FSP_BIT_DRAG_LOCK BIT(6)
+#define FSP_REG_SWC1 (0x90)
+#define FSP_BIT_SWC1_EN_ABS_1F BIT(0)
+#define FSP_BIT_SWC1_EN_GID BIT(1)
+#define FSP_BIT_SWC1_EN_ABS_2F BIT(2)
+#define FSP_BIT_SWC1_EN_FUP_OUT BIT(3)
+#define FSP_BIT_SWC1_EN_ABS_CON BIT(4)
+#define FSP_BIT_SWC1_GST_GRP0 BIT(5)
+#define FSP_BIT_SWC1_GST_GRP1 BIT(6)
+#define FSP_BIT_SWC1_BX_COMPAT BIT(7)
+
/* Finger-sensing Pad packet formating related definitions */
/* absolute packet type */
@@ -64,12 +74,32 @@
#define FSP_PKT_TYPE_NORMAL_OPC (0x03)
#define FSP_PKT_TYPE_SHIFT (6)
+/* bit definitions for the first byte of report packet */
+#define FSP_PB0_LBTN BIT(0)
+#define FSP_PB0_RBTN BIT(1)
+#define FSP_PB0_MBTN BIT(2)
+#define FSP_PB0_MFMC_FGR2 FSP_PB0_MBTN
+#define FSP_PB0_MUST_SET BIT(3)
+#define FSP_PB0_PHY_BTN BIT(4)
+#define FSP_PB0_MFMC BIT(5)
+
+/* hardware revisions */
+#define FSP_VER_STL3888_A4 (0xC1)
+#define FSP_VER_STL3888_B0 (0xD0)
+#define FSP_VER_STL3888_B1 (0xD1)
+#define FSP_VER_STL3888_B2 (0xD2)
+#define FSP_VER_STL3888_C0 (0xE0)
+#define FSP_VER_STL3888_C1 (0xE1)
+#define FSP_VER_STL3888_D0 (0xE2)
+#define FSP_VER_STL3888_D1 (0xE3)
+#define FSP_VER_STL3888_E0 (0xE4)
+
#ifdef __KERNEL__
struct fsp_data {
unsigned char ver; /* hardware version */
unsigned char rev; /* hardware revison */
- unsigned char buttons; /* Number of buttons */
+ unsigned int buttons; /* Number of buttons */
unsigned int flags;
#define FSPDRV_FLAG_EN_OPC (0x001) /* enable on-pad clicking */
@@ -78,6 +108,7 @@ struct fsp_data {
unsigned char last_reg; /* Last register we requested read from */
unsigned char last_val;
+ unsigned int last_mt_fgr; /* Last seen finger(multitouch) */
};
#ifdef CONFIG_MOUSE_PS2_SENTELIC
diff --git a/drivers/input/serio/ams_delta_serio.c b/drivers/input/serio/ams_delta_serio.c
index bd5b10e..f5fbdf9 100644
--- a/drivers/input/serio/ams_delta_serio.c
+++ b/drivers/input/serio/ams_delta_serio.c
@@ -184,7 +184,7 @@ module_init(ams_delta_serio_init);
static void __exit ams_delta_serio_exit(void)
{
serio_unregister_port(ams_delta_serio);
- free_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0);
+ free_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0);
gpio_free_array(ams_delta_gpios,
ARRAY_SIZE(ams_delta_gpios));
}
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index be33160..09a0899 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -71,7 +71,6 @@
#include <linux/slab.h>
#include <linux/hil.h>
#include <asm/io.h>
-#include <asm/system.h>
/* Machine-specific abstraction */
diff --git a/drivers/input/serio/maceps2.c b/drivers/input/serio/maceps2.c
index 558200e..61da763 100644
--- a/drivers/input/serio/maceps2.c
+++ b/drivers/input/serio/maceps2.c
@@ -21,7 +21,6 @@
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/ip32/mace.h>
#include <asm/ip32/ip32_ints.h>
diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c
index 8b44ddc..2af5df6 100644
--- a/drivers/input/serio/rpckbd.c
+++ b/drivers/input/serio/rpckbd.c
@@ -36,16 +36,19 @@
#include <linux/io.h>
#include <linux/slab.h>
-#include <asm/irq.h>
#include <mach/hardware.h>
#include <asm/hardware/iomd.h>
-#include <asm/system.h>
MODULE_AUTHOR("Vojtech Pavlik, Russell King");
MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:kart");
+struct rpckbd_data {
+ int tx_irq;
+ int rx_irq;
+};
+
static int rpckbd_write(struct serio *port, unsigned char val)
{
while (!(iomd_readb(IOMD_KCTRL) & (1 << 7)))
@@ -78,19 +81,21 @@ static irqreturn_t rpckbd_tx(int irq, void *dev_id)
static int rpckbd_open(struct serio *port)
{
+ struct rpckbd_data *rpckbd = port->port_data;
+
/* Reset the keyboard state machine. */
iomd_writeb(0, IOMD_KCTRL);
iomd_writeb(8, IOMD_KCTRL);
iomd_readb(IOMD_KARTRX);
- if (request_irq(IRQ_KEYBOARDRX, rpckbd_rx, 0, "rpckbd", port) != 0) {
+ if (request_irq(rpckbd->rx_irq, rpckbd_rx, 0, "rpckbd", port) != 0) {
printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ\n");
return -EBUSY;
}
- if (request_irq(IRQ_KEYBOARDTX, rpckbd_tx, 0, "rpckbd", port) != 0) {
+ if (request_irq(rpckbd->tx_irq, rpckbd_tx, 0, "rpckbd", port) != 0) {
printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ\n");
- free_irq(IRQ_KEYBOARDRX, port);
+ free_irq(rpckbd->rx_irq, port);
return -EBUSY;
}
@@ -99,8 +104,10 @@ static int rpckbd_open(struct serio *port)
static void rpckbd_close(struct serio *port)
{
- free_irq(IRQ_KEYBOARDRX, port);
- free_irq(IRQ_KEYBOARDTX, port);
+ struct rpckbd_data *rpckbd = port->port_data;
+
+ free_irq(rpckbd->rx_irq, port);
+ free_irq(rpckbd->tx_irq, port);
}
/*
@@ -109,17 +116,35 @@ static void rpckbd_close(struct serio *port)
*/
static int __devinit rpckbd_probe(struct platform_device *dev)
{
+ struct rpckbd_data *rpckbd;
struct serio *serio;
+ int tx_irq, rx_irq;
+
+ rx_irq = platform_get_irq(dev, 0);
+ if (rx_irq <= 0)
+ return rx_irq < 0 ? rx_irq : -ENXIO;
+
+ tx_irq = platform_get_irq(dev, 1);
+ if (tx_irq <= 0)
+ return tx_irq < 0 ? tx_irq : -ENXIO;
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
- if (!serio)
+ rpckbd = kzalloc(sizeof(*rpckbd), GFP_KERNEL);
+ if (!serio || !rpckbd) {
+ kfree(rpckbd);
+ kfree(serio);
return -ENOMEM;
+ }
+
+ rpckbd->rx_irq = rx_irq;
+ rpckbd->tx_irq = tx_irq;
serio->id.type = SERIO_8042;
serio->write = rpckbd_write;
serio->open = rpckbd_open;
serio->close = rpckbd_close;
serio->dev.parent = &dev->dev;
+ serio->port_data = rpckbd;
strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name));
strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys));
@@ -131,7 +156,11 @@ static int __devinit rpckbd_probe(struct platform_device *dev)
static int __devexit rpckbd_remove(struct platform_device *dev)
{
struct serio *serio = platform_get_drvdata(dev);
+ struct rpckbd_data *rpckbd = serio->port_data;
+
serio_unregister_port(serio);
+ kfree(rpckbd);
+
return 0;
}
diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c
index 44fc8b4..3897667 100644
--- a/drivers/input/serio/sa1111ps2.c
+++ b/drivers/input/serio/sa1111ps2.c
@@ -20,10 +20,29 @@
#include <linux/spinlock.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/hardware/sa1111.h>
+#define PS2CR 0x0000
+#define PS2STAT 0x0004
+#define PS2DATA 0x0008
+#define PS2CLKDIV 0x000c
+#define PS2PRECNT 0x0010
+
+#define PS2CR_ENA 0x08
+#define PS2CR_FKD 0x02
+#define PS2CR_FKC 0x01
+
+#define PS2STAT_STP 0x0100
+#define PS2STAT_TXE 0x0080
+#define PS2STAT_TXB 0x0040
+#define PS2STAT_RXF 0x0020
+#define PS2STAT_RXB 0x0010
+#define PS2STAT_ENA 0x0008
+#define PS2STAT_RXP 0x0004
+#define PS2STAT_KBD 0x0002
+#define PS2STAT_KBC 0x0001
+
struct ps2if {
struct serio *io;
struct sa1111_dev *dev;
@@ -45,22 +64,22 @@ static irqreturn_t ps2_rxint(int irq, void *dev_id)
struct ps2if *ps2if = dev_id;
unsigned int scancode, flag, status;
- status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
+ status = sa1111_readl(ps2if->base + PS2STAT);
while (status & PS2STAT_RXF) {
if (status & PS2STAT_STP)
- sa1111_writel(PS2STAT_STP, ps2if->base + SA1111_PS2STAT);
+ sa1111_writel(PS2STAT_STP, ps2if->base + PS2STAT);
flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) |
(status & PS2STAT_RXP ? 0 : SERIO_PARITY);
- scancode = sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff;
+ scancode = sa1111_readl(ps2if->base + PS2DATA) & 0xff;
if (hweight8(scancode) & 1)
flag ^= SERIO_PARITY;
serio_interrupt(ps2if->io, scancode, flag);
- status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
+ status = sa1111_readl(ps2if->base + PS2STAT);
}
return IRQ_HANDLED;
@@ -75,12 +94,12 @@ static irqreturn_t ps2_txint(int irq, void *dev_id)
unsigned int status;
spin_lock(&ps2if->lock);
- status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
+ status = sa1111_readl(ps2if->base + PS2STAT);
if (ps2if->head == ps2if->tail) {
disable_irq_nosync(irq);
/* done */
} else if (status & PS2STAT_TXE) {
- sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + SA1111_PS2DATA);
+ sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + PS2DATA);
ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1);
}
spin_unlock(&ps2if->lock);
@@ -103,8 +122,8 @@ static int ps2_write(struct serio *io, unsigned char val)
/*
* If the TX register is empty, we can go straight out.
*/
- if (sa1111_readl(ps2if->base + SA1111_PS2STAT) & PS2STAT_TXE) {
- sa1111_writel(val, ps2if->base + SA1111_PS2DATA);
+ if (sa1111_readl(ps2if->base + PS2STAT) & PS2STAT_TXE) {
+ sa1111_writel(val, ps2if->base + PS2DATA);
} else {
if (ps2if->head == ps2if->tail)
enable_irq(ps2if->dev->irq[1]);
@@ -124,13 +143,16 @@ static int ps2_open(struct serio *io)
struct ps2if *ps2if = io->port_data;
int ret;
- sa1111_enable_device(ps2if->dev);
+ ret = sa1111_enable_device(ps2if->dev);
+ if (ret)
+ return ret;
ret = request_irq(ps2if->dev->irq[0], ps2_rxint, 0,
SA1111_DRIVER_NAME(ps2if->dev), ps2if);
if (ret) {
printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
ps2if->dev->irq[0], ret);
+ sa1111_disable_device(ps2if->dev);
return ret;
}
@@ -140,6 +162,7 @@ static int ps2_open(struct serio *io)
printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
ps2if->dev->irq[1], ret);
free_irq(ps2if->dev->irq[0], ps2if);
+ sa1111_disable_device(ps2if->dev);
return ret;
}
@@ -147,7 +170,7 @@ static int ps2_open(struct serio *io)
enable_irq_wake(ps2if->dev->irq[0]);
- sa1111_writel(PS2CR_ENA, ps2if->base + SA1111_PS2CR);
+ sa1111_writel(PS2CR_ENA, ps2if->base + PS2CR);
return 0;
}
@@ -155,7 +178,7 @@ static void ps2_close(struct serio *io)
{
struct ps2if *ps2if = io->port_data;
- sa1111_writel(0, ps2if->base + SA1111_PS2CR);
+ sa1111_writel(0, ps2if->base + PS2CR);
disable_irq_wake(ps2if->dev->irq[0]);
@@ -175,7 +198,7 @@ static void __devinit ps2_clear_input(struct ps2if *ps2if)
int maxread = 100;
while (maxread--) {
- if ((sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff) == 0xff)
+ if ((sa1111_readl(ps2if->base + PS2DATA) & 0xff) == 0xff)
break;
}
}
@@ -185,11 +208,11 @@ static unsigned int __devinit ps2_test_one(struct ps2if *ps2if,
{
unsigned int val;
- sa1111_writel(PS2CR_ENA | mask, ps2if->base + SA1111_PS2CR);
+ sa1111_writel(PS2CR_ENA | mask, ps2if->base + PS2CR);
udelay(2);
- val = sa1111_readl(ps2if->base + SA1111_PS2STAT);
+ val = sa1111_readl(ps2if->base + PS2STAT);
return val & (PS2STAT_KBC | PS2STAT_KBD);
}
@@ -220,7 +243,7 @@ static int __devinit ps2_test(struct ps2if *ps2if)
ret = -ENODEV;
}
- sa1111_writel(0, ps2if->base + SA1111_PS2CR);
+ sa1111_writel(0, ps2if->base + PS2CR);
return ret;
}
@@ -274,8 +297,8 @@ static int __devinit ps2_probe(struct sa1111_dev *dev)
sa1111_enable_device(ps2if->dev);
/* Incoming clock is 8MHz */
- sa1111_writel(0, ps2if->base + SA1111_PS2CLKDIV);
- sa1111_writel(127, ps2if->base + SA1111_PS2PRECNT);
+ sa1111_writel(0, ps2if->base + PS2CLKDIV);
+ sa1111_writel(127, ps2if->base + PS2PRECNT);
/*
* Flush any pending input.
@@ -330,6 +353,7 @@ static int __devexit ps2_remove(struct sa1111_dev *dev)
static struct sa1111_driver ps2_driver = {
.drv = {
.name = "sa1111-ps2",
+ .owner = THIS_MODULE,
},
.devid = SA1111_DEVID_PS2,
.probe = ps2_probe,
diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig
index e53f408..bed7cbf 100644
--- a/drivers/input/tablet/Kconfig
+++ b/drivers/input/tablet/Kconfig
@@ -76,6 +76,7 @@ config TABLET_USB_KBTAB
config TABLET_USB_WACOM
tristate "Wacom Intuos/Graphire tablet support (USB)"
depends on USB_ARCH_HAS_HCD
+ select POWER_SUPPLY
select USB
select NEW_LEDS
select LEDS_CLASS
diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h
index 0783864..b4842d0 100644
--- a/drivers/input/tablet/wacom.h
+++ b/drivers/input/tablet/wacom.h
@@ -88,6 +88,7 @@
#include <linux/mod_devicetable.h>
#include <linux/init.h>
#include <linux/usb/input.h>
+#include <linux/power_supply.h>
#include <asm/unaligned.h>
/*
@@ -112,6 +113,7 @@ struct wacom {
struct urb *irq;
struct wacom_wac wacom_wac;
struct mutex lock;
+ struct work_struct work;
bool open;
char phys[32];
struct wacom_led {
@@ -120,8 +122,15 @@ struct wacom {
u8 hlv; /* status led brightness button pressed (1..127) */
u8 img_lum; /* OLED matrix display brightness */
} led;
+ struct power_supply battery;
};
+static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
+{
+ struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+ schedule_work(&wacom->work);
+}
+
extern const struct usb_device_id wacom_ids[];
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index ca28066..0d26921 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -167,6 +167,19 @@ static void wacom_close(struct input_dev *dev)
usb_autopm_put_interface(wacom->intf);
}
+/*
+ * Static values for max X/Y and resolution of Pen interface is stored in
+ * features. This mean physical size of active area can be computed.
+ * This is useful to do when Pen and Touch have same active area of tablet.
+ * This means for Touch device, we only need to find max X/Y value and we
+ * have enough information to compute resolution of touch.
+ */
+static void wacom_set_phy_from_res(struct wacom_features *features)
+{
+ features->x_phy = (features->x_max * 100) / features->x_resolution;
+ features->y_phy = (features->y_max * 100) / features->y_resolution;
+}
+
static int wacom_parse_logical_collection(unsigned char *report,
struct wacom_features *features)
{
@@ -178,15 +191,7 @@ static int wacom_parse_logical_collection(unsigned char *report,
features->pktlen = WACOM_PKGLEN_BBTOUCH3;
features->device_type = BTN_TOOL_FINGER;
- /*
- * Stylus and Touch have same active area
- * so compute physical size based on stylus
- * data before its overwritten.
- */
- features->x_phy =
- (features->x_max * 100) / features->x_resolution;
- features->y_phy =
- (features->y_max * 100) / features->y_resolution;
+ wacom_set_phy_from_res(features);
features->x_max = features->y_max =
get_unaligned_le16(&report[10]);
@@ -422,6 +427,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
report_id, rep_data, 4, 1);
} while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES);
} else if (features->type != TABLETPC &&
+ features->type != WIRELESS &&
features->device_type == BTN_TOOL_PEN) {
do {
rep_data[0] = 2;
@@ -454,6 +460,21 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
features->pressure_fuzz = 0;
features->distance_fuzz = 0;
+ /*
+ * The wireless device HID is basic and layout conflicts with
+ * other tablets (monitor and touch interface can look like pen).
+ * Skip the query for this type and modify defaults based on
+ * interface number.
+ */
+ if (features->type == WIRELESS) {
+ if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
+ features->device_type = 0;
+ } else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) {
+ features->device_type = BTN_TOOL_DOUBLETAP;
+ features->pktlen = WACOM_PKGLEN_BBTOUCH3;
+ }
+ }
+
/* only Tablet PCs and Bamboo P&T need to retrieve the info */
if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) &&
(features->type != BAMBOO_PT))
@@ -822,6 +843,152 @@ static void wacom_destroy_leds(struct wacom *wacom)
}
}
+static enum power_supply_property wacom_battery_props[] = {
+ POWER_SUPPLY_PROP_CAPACITY
+};
+
+static int wacom_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct wacom *wacom = container_of(psy, struct wacom, battery);
+ int ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval =
+ wacom->wacom_wac.battery_capacity * 100 / 31;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int wacom_initialize_battery(struct wacom *wacom)
+{
+ int error = 0;
+
+ if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) {
+ wacom->battery.properties = wacom_battery_props;
+ wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
+ wacom->battery.get_property = wacom_battery_get_property;
+ wacom->battery.name = "wacom_battery";
+ wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+ wacom->battery.use_for_apm = 0;
+
+ error = power_supply_register(&wacom->usbdev->dev,
+ &wacom->battery);
+ }
+
+ return error;
+}
+
+static void wacom_destroy_battery(struct wacom *wacom)
+{
+ if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR)
+ power_supply_unregister(&wacom->battery);
+}
+
+static int wacom_register_input(struct wacom *wacom)
+{
+ struct input_dev *input_dev;
+ struct usb_interface *intf = wacom->intf;
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
+ int error;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = wacom_wac->name;
+ input_dev->dev.parent = &intf->dev;
+ input_dev->open = wacom_open;
+ input_dev->close = wacom_close;
+ usb_to_input_id(dev, &input_dev->id);
+ input_set_drvdata(input_dev, wacom);
+
+ wacom_wac->input = input_dev;
+ wacom_setup_input_capabilities(input_dev, wacom_wac);
+
+ error = input_register_device(input_dev);
+ if (error) {
+ input_free_device(input_dev);
+ wacom_wac->input = NULL;
+ }
+
+ return error;
+}
+
+static void wacom_wireless_work(struct work_struct *work)
+{
+ struct wacom *wacom = container_of(work, struct wacom, work);
+ struct usb_device *usbdev = wacom->usbdev;
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+
+ /*
+ * Regardless if this is a disconnect or a new tablet,
+ * remove any existing input devices.
+ */
+
+ /* Stylus interface */
+ wacom = usb_get_intfdata(usbdev->config->interface[1]);
+ if (wacom->wacom_wac.input)
+ input_unregister_device(wacom->wacom_wac.input);
+ wacom->wacom_wac.input = 0;
+
+ /* Touch interface */
+ wacom = usb_get_intfdata(usbdev->config->interface[2]);
+ if (wacom->wacom_wac.input)
+ input_unregister_device(wacom->wacom_wac.input);
+ wacom->wacom_wac.input = 0;
+
+ if (wacom_wac->pid == 0) {
+ printk(KERN_INFO "wacom: wireless tablet disconnected\n");
+ } else {
+ const struct usb_device_id *id = wacom_ids;
+
+ printk(KERN_INFO
+ "wacom: wireless tablet connected with PID %x\n",
+ wacom_wac->pid);
+
+ while (id->match_flags) {
+ if (id->idVendor == USB_VENDOR_ID_WACOM &&
+ id->idProduct == wacom_wac->pid)
+ break;
+ id++;
+ }
+
+ if (!id->match_flags) {
+ printk(KERN_INFO
+ "wacom: ignorning unknown PID.\n");
+ return;
+ }
+
+ /* Stylus interface */
+ wacom = usb_get_intfdata(usbdev->config->interface[1]);
+ wacom_wac = &wacom->wacom_wac;
+ wacom_wac->features =
+ *((struct wacom_features *)id->driver_info);
+ wacom_wac->features.device_type = BTN_TOOL_PEN;
+ wacom_register_input(wacom);
+
+ /* Touch interface */
+ wacom = usb_get_intfdata(usbdev->config->interface[2]);
+ wacom_wac = &wacom->wacom_wac;
+ wacom_wac->features =
+ *((struct wacom_features *)id->driver_info);
+ wacom_wac->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
+ wacom_wac->features.device_type = BTN_TOOL_FINGER;
+ wacom_set_phy_from_res(&wacom_wac->features);
+ wacom_wac->features.x_max = wacom_wac->features.y_max = 4096;
+ wacom_register_input(wacom);
+ }
+}
+
static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
@@ -829,18 +996,14 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
struct wacom *wacom;
struct wacom_wac *wacom_wac;
struct wacom_features *features;
- struct input_dev *input_dev;
int error;
if (!id->driver_info)
return -EINVAL;
wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!wacom || !input_dev) {
- error = -ENOMEM;
- goto fail1;
- }
+ if (!wacom)
+ return -ENOMEM;
wacom_wac = &wacom->wacom_wac;
wacom_wac->features = *((struct wacom_features *)id->driver_info);
@@ -866,11 +1029,10 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
wacom->usbdev = dev;
wacom->intf = intf;
mutex_init(&wacom->lock);
+ INIT_WORK(&wacom->work, wacom_wireless_work);
usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
- wacom_wac->input = input_dev;
-
endpoint = &intf->cur_altsetting->endpoint[0].desc;
/* Retrieve the physical and logical size for OEM devices */
@@ -894,15 +1056,6 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
goto fail3;
}
- input_dev->name = wacom_wac->name;
- input_dev->dev.parent = &intf->dev;
- input_dev->open = wacom_open;
- input_dev->close = wacom_close;
- usb_to_input_id(dev, &input_dev->id);
- input_set_drvdata(input_dev, wacom);
-
- wacom_setup_input_capabilities(input_dev, wacom_wac);
-
usb_fill_int_urb(wacom->irq, dev,
usb_rcvintpipe(dev, endpoint->bEndpointAddress),
wacom_wac->data, features->pktlen,
@@ -914,22 +1067,34 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
if (error)
goto fail4;
- error = input_register_device(input_dev);
+ error = wacom_initialize_battery(wacom);
if (error)
goto fail5;
+ if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
+ error = wacom_register_input(wacom);
+ if (error)
+ goto fail6;
+ }
+
/* Note that if query fails it is not a hard failure */
wacom_query_tablet_data(intf, features);
usb_set_intfdata(intf, wacom);
+
+ if (features->quirks & WACOM_QUIRK_MONITOR) {
+ if (usb_submit_urb(wacom->irq, GFP_KERNEL))
+ goto fail5;
+ }
+
return 0;
+ fail6: wacom_destroy_battery(wacom);
fail5: wacom_destroy_leds(wacom);
fail4: wacom_remove_shared_data(wacom_wac);
fail3: usb_free_urb(wacom->irq);
fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma);
- fail1: input_free_device(input_dev);
- kfree(wacom);
+ fail1: kfree(wacom);
return error;
}
@@ -940,7 +1105,10 @@ static void wacom_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
usb_kill_urb(wacom->irq);
- input_unregister_device(wacom->wacom_wac.input);
+ cancel_work_sync(&wacom->work);
+ if (wacom->wacom_wac.input)
+ input_unregister_device(wacom->wacom_wac.input);
+ wacom_destroy_battery(wacom);
wacom_destroy_leds(wacom);
usb_free_urb(wacom->irq);
usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,
@@ -972,7 +1140,8 @@ static int wacom_resume(struct usb_interface *intf)
wacom_query_tablet_data(intf, features);
wacom_led_control(wacom);
- if (wacom->open && usb_submit_urb(wacom->irq, GFP_NOIO) < 0)
+ if ((wacom->open || features->quirks & WACOM_QUIRK_MONITOR)
+ && usb_submit_urb(wacom->irq, GFP_NOIO) < 0)
rv = -EIO;
mutex_unlock(&wacom->lock);
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 89a96427..cecd35c 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -1044,6 +1044,35 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
return 0;
}
+static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
+{
+ unsigned char *data = wacom->data;
+ int connected;
+
+ if (len != WACOM_PKGLEN_WIRELESS || data[0] != 0x80)
+ return 0;
+
+ connected = data[1] & 0x01;
+ if (connected) {
+ int pid, battery;
+
+ pid = get_unaligned_be16(&data[6]);
+ battery = data[5] & 0x3f;
+ if (wacom->pid != pid) {
+ wacom->pid = pid;
+ wacom_schedule_work(wacom);
+ }
+ wacom->battery_capacity = battery;
+ } else if (wacom->pid != 0) {
+ /* disconnected while previously connected */
+ wacom->pid = 0;
+ wacom_schedule_work(wacom);
+ wacom->battery_capacity = 0;
+ }
+
+ return 0;
+}
+
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
{
bool sync;
@@ -1094,6 +1123,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
sync = wacom_bpt_irq(wacom_wac, len);
break;
+ case WIRELESS:
+ sync = wacom_wireless_irq(wacom_wac, len);
+ break;
+
default:
sync = false;
break;
@@ -1155,7 +1188,7 @@ void wacom_setup_device_quirks(struct wacom_features *features)
/* these device have multiple inputs */
if (features->type == TABLETPC || features->type == TABLETPC2FG ||
- features->type == BAMBOO_PT)
+ features->type == BAMBOO_PT || features->type == WIRELESS)
features->quirks |= WACOM_QUIRK_MULTI_INPUT;
/* quirk for bamboo touch with 2 low res touches */
@@ -1167,6 +1200,16 @@ void wacom_setup_device_quirks(struct wacom_features *features)
features->y_fuzz <<= 5;
features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES;
}
+
+ if (features->type == WIRELESS) {
+
+ /* monitor never has input and pen/touch have delayed create */
+ features->quirks |= WACOM_QUIRK_NO_INPUT;
+
+ /* must be monitor interface if no device_type set */
+ if (!features->device_type)
+ features->quirks |= WACOM_QUIRK_MONITOR;
+ }
}
static unsigned int wacom_calculate_touch_res(unsigned int logical_max,
@@ -1640,6 +1683,9 @@ static const struct wacom_features wacom_features_0xEC =
static const struct wacom_features wacom_features_0x47 =
{ "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023,
31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x84 =
+ { "Wacom Wireless Receiver", WACOM_PKGLEN_WIRELESS, 0, 0, 0,
+ 0, WIRELESS, 0, 0 };
static const struct wacom_features wacom_features_0xD0 =
{ "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -1766,6 +1812,7 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_DETAILED(0xCE, USB_CLASS_HID,
USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
+ { USB_DEVICE_WACOM(0x84) },
{ USB_DEVICE_WACOM(0xD0) },
{ USB_DEVICE_WACOM(0xD1) },
{ USB_DEVICE_WACOM(0xD2) },
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
index 4f0ba21..ba5a334 100644
--- a/drivers/input/tablet/wacom_wac.h
+++ b/drivers/input/tablet/wacom_wac.h
@@ -24,6 +24,7 @@
#define WACOM_PKGLEN_BBTOUCH 20
#define WACOM_PKGLEN_BBTOUCH3 64
#define WACOM_PKGLEN_BBPEN 10
+#define WACOM_PKGLEN_WIRELESS 32
/* device IDs */
#define STYLUS_DEVICE_ID 0x02
@@ -45,6 +46,8 @@
/* device quirks */
#define WACOM_QUIRK_MULTI_INPUT 0x0001
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002
+#define WACOM_QUIRK_NO_INPUT 0x0004
+#define WACOM_QUIRK_MONITOR 0x0008
enum {
PENPARTNER = 0,
@@ -54,6 +57,7 @@ enum {
PL,
DTU,
BAMBOO_PT,
+ WIRELESS,
INTUOS,
INTUOS3S,
INTUOS3,
@@ -107,6 +111,8 @@ struct wacom_wac {
struct wacom_features features;
struct wacom_shared *shared;
struct input_dev *input;
+ int pid;
+ int battery_capacity;
};
#endif
diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c
index c3848ad..d9be6ea 100644
--- a/drivers/input/touchscreen/jornada720_ts.c
+++ b/drivers/input/touchscreen/jornada720_ts.c
@@ -22,6 +22,7 @@
#include <mach/hardware.h>
#include <mach/jornada720.h>
+#include <mach/irqs.h>
MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver");
diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c
index ede0274..48dc5b0 100644
--- a/drivers/input/touchscreen/mc13783_ts.c
+++ b/drivers/input/touchscreen/mc13783_ts.c
@@ -39,6 +39,7 @@ struct mc13783_ts_priv {
struct delayed_work work;
struct workqueue_struct *workq;
unsigned int sample[4];
+ struct mc13xxx_ts_platform_data *touch;
};
static irqreturn_t mc13783_ts_handler(int irq, void *data)
@@ -125,7 +126,9 @@ static void mc13783_ts_work(struct work_struct *work)
unsigned int channel = 12;
if (mc13xxx_adc_do_conversion(priv->mc13xxx,
- mode, channel, priv->sample) == 0)
+ mode, channel,
+ priv->touch->ato, priv->touch->atox,
+ priv->sample) == 0)
mc13783_ts_report_sample(priv);
}
@@ -179,6 +182,12 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&priv->work, mc13783_ts_work);
priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
priv->idev = idev;
+ priv->touch = dev_get_platdata(&pdev->dev);
+ if (!priv->touch) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ ret = -ENODEV;
+ goto err_free_mem;
+ }
/*
* We need separate workqueue because mc13783_adc_do_conversion
diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c
index 44b50cc..c21353d 100644
--- a/drivers/isdn/hardware/avm/avm_cs.c
+++ b/drivers/isdn/hardware/avm/avm_cs.c
@@ -18,7 +18,6 @@
#include <linux/serial.h>
#include <linux/major.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c
index 33e3c94..c644557 100644
--- a/drivers/isdn/hisax/avma1_cs.c
+++ b/drivers/isdn/hisax/avma1_cs.c
@@ -18,7 +18,6 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c
index fe254e7..a8c4d3f 100644
--- a/drivers/isdn/hisax/elsa_cs.c
+++ b/drivers/isdn/hisax/elsa_cs.c
@@ -44,7 +44,6 @@
#include <linux/timer.h>
#include <linux/ioport.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c
index 68f5049..f0dfc0c 100644
--- a/drivers/isdn/hisax/sedlbauer_cs.c
+++ b/drivers/isdn/hisax/sedlbauer_cs.c
@@ -44,7 +44,6 @@
#include <linux/timer.h>
#include <linux/ioport.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c
index bfe9428..4deac45 100644
--- a/drivers/isdn/hisax/teles_cs.c
+++ b/drivers/isdn/hisax/teles_cs.c
@@ -25,7 +25,6 @@
#include <linux/timer.h>
#include <linux/ioport.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
diff --git a/drivers/isdn/i4l/isdn_bsdcomp.c b/drivers/isdn/i4l/isdn_bsdcomp.c
index 7f3c54d..c59e8d2 100644
--- a/drivers/isdn/i4l/isdn_bsdcomp.c
+++ b/drivers/isdn/i4l/isdn_bsdcomp.c
@@ -69,7 +69,6 @@
#include <linux/signal.h> /* used in new tty drivers */
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
#include <asm/types.h>
diff --git a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c
index 682911f..a18e639 100644
--- a/drivers/isdn/pcbit/layer2.c
+++ b/drivers/isdn/pcbit/layer2.c
@@ -36,7 +36,6 @@
#include <linux/isdnif.h>
-#include <asm/system.h>
#include <asm/io.h>
diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c
index 4ca0062..5b61aaf 100644
--- a/drivers/leds/leds-88pm860x.c
+++ b/drivers/leds/leds-88pm860x.c
@@ -114,6 +114,27 @@ static inline int __blink_ctl_mask(int port)
return ret;
}
+static int led_power_set(struct pm860x_chip *chip, int port, int on)
+{
+ int ret = -EINVAL;
+
+ switch (port) {
+ case PM8606_LED1_RED:
+ case PM8606_LED1_GREEN:
+ case PM8606_LED1_BLUE:
+ ret = on ? pm8606_osc_enable(chip, RGB1_ENABLE) :
+ pm8606_osc_disable(chip, RGB1_ENABLE);
+ break;
+ case PM8606_LED2_RED:
+ case PM8606_LED2_GREEN:
+ case PM8606_LED2_BLUE:
+ ret = on ? pm8606_osc_enable(chip, RGB2_ENABLE) :
+ pm8606_osc_disable(chip, RGB2_ENABLE);
+ break;
+ }
+ return ret;
+}
+
static void pm860x_led_work(struct work_struct *work)
{
@@ -126,6 +147,7 @@ static void pm860x_led_work(struct work_struct *work)
chip = led->chip;
mutex_lock(&led->lock);
if ((led->current_brightness == 0) && led->brightness) {
+ led_power_set(chip, led->port, 1);
if (led->iset) {
pm860x_set_bits(led->i2c, __led_off(led->port),
LED_CURRENT_MASK, led->iset);
@@ -149,6 +171,7 @@ static void pm860x_led_work(struct work_struct *work)
LED_CURRENT_MASK, 0);
mask = __blink_ctl_mask(led->port);
pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0);
+ led_power_set(chip, led->port, 0);
}
}
led->current_brightness = led->brightness;
diff --git a/drivers/macintosh/macio-adb.c b/drivers/macintosh/macio-adb.c
index b6ef8f5..87de8d9 100644
--- a/drivers/macintosh/macio-adb.c
+++ b/drivers/macintosh/macio-adb.c
@@ -14,7 +14,6 @@
#include <asm/pgtable.h>
#include <asm/hydra.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <linux/init.h>
#include <linux/ioport.h>
diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c
index c60d025..fc71723 100644
--- a/drivers/macintosh/therm_adt746x.c
+++ b/drivers/macintosh/therm_adt746x.c
@@ -29,7 +29,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#undef DEBUG
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
index 0ff92c2..97cfc5a 100644
--- a/drivers/macintosh/therm_pm72.c
+++ b/drivers/macintosh/therm_pm72.c
@@ -127,7 +127,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/macio.h>
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c
index 46c4e95..3b4a157 100644
--- a/drivers/macintosh/therm_windtunnel.c
+++ b/drivers/macintosh/therm_windtunnel.c
@@ -41,7 +41,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/macio.h>
diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c
index 971bc95..86511c5 100644
--- a/drivers/macintosh/via-cuda.c
+++ b/drivers/macintosh/via-cuda.c
@@ -26,7 +26,6 @@
#include <asm/mac_via.h>
#endif
#include <asm/io.h>
-#include <asm/system.h>
#include <linux/init.h>
static volatile unsigned char __iomem *via;
diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c
index c9570fc..3725f08 100644
--- a/drivers/macintosh/via-macii.c
+++ b/drivers/macintosh/via-macii.c
@@ -34,7 +34,6 @@
#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_via.h>
-#include <asm/system.h>
static volatile unsigned char *via;
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 6cccd60..22b8ce4 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -50,7 +50,6 @@
#include <asm/machdep.h>
#include <asm/io.h>
#include <asm/pgtable.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/irq.h>
#include <asm/pmac_feature.h>
diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c
index aeb30d0..a00ee41 100644
--- a/drivers/macintosh/via-pmu68k.c
+++ b/drivers/macintosh/via-pmu68k.c
@@ -37,7 +37,6 @@
#include <asm/mac_via.h>
#include <asm/pgtable.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c
index 647c6ad..4d6a90a 100644
--- a/drivers/macintosh/windfarm_lm75_sensor.c
+++ b/drivers/macintosh/windfarm_lm75_sensor.c
@@ -18,7 +18,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/pmac_low_i2c.h>
diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c
index 30e6195..04067e0 100644
--- a/drivers/macintosh/windfarm_pm121.c
+++ b/drivers/macintosh/windfarm_pm121.c
@@ -215,7 +215,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/smu.h>
diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c
index 749d174..fc13d0f 100644
--- a/drivers/macintosh/windfarm_pm81.c
+++ b/drivers/macintosh/windfarm_pm81.c
@@ -107,7 +107,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/smu.h>
diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c
index 3442732..a9430ed 100644
--- a/drivers/macintosh/windfarm_pm91.c
+++ b/drivers/macintosh/windfarm_pm91.c
@@ -41,7 +41,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/smu.h>
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c
index 43137b4..3c2be51 100644
--- a/drivers/macintosh/windfarm_smu_controls.c
+++ b/drivers/macintosh/windfarm_smu_controls.c
@@ -18,7 +18,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/smu.h>
diff --git a/drivers/macintosh/windfarm_smu_sensors.c b/drivers/macintosh/windfarm_smu_sensors.c
index 3c19350..1cc4e49 100644
--- a/drivers/macintosh/windfarm_smu_sensors.c
+++ b/drivers/macintosh/windfarm_smu_sensors.c
@@ -18,7 +18,6 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sections.h>
#include <asm/smu.h>
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index faa4741..10f122a 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -277,8 +277,8 @@ config DM_MIRROR
needed for live data migration tools such as 'pvmove'.
config DM_RAID
- tristate "RAID 1/4/5/6 target (EXPERIMENTAL)"
- depends on BLK_DEV_DM && EXPERIMENTAL
+ tristate "RAID 1/4/5/6 target"
+ depends on BLK_DEV_DM
select MD_RAID1
select MD_RAID456
select BLK_DEV_MD
@@ -359,8 +359,8 @@ config DM_DELAY
If unsure, say N.
config DM_UEVENT
- bool "DM uevents (EXPERIMENTAL)"
- depends on BLK_DEV_DM && EXPERIMENTAL
+ bool "DM uevents"
+ depends on BLK_DEV_DM
---help---
Generate udev events for DM events.
@@ -370,4 +370,24 @@ config DM_FLAKEY
---help---
A target that intermittently fails I/O for debugging purposes.
+config DM_VERITY
+ tristate "Verity target support (EXPERIMENTAL)"
+ depends on BLK_DEV_DM && EXPERIMENTAL
+ select CRYPTO
+ select CRYPTO_HASH
+ select DM_BUFIO
+ ---help---
+ This device-mapper target creates a read-only device that
+ transparently validates the data on one underlying device against
+ a pre-generated tree of cryptographic checksums stored on a second
+ device.
+
+ You'll need to activate the digests you're going to use in the
+ cryptoapi configuration.
+
+ To compile this code as a module, choose M here: the module will
+ be called dm-verity.
+
+ If unsure, say N.
+
endif # MD
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 046860c..8b2e0df 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_DM_LOG_USERSPACE) += dm-log-userspace.o
obj-$(CONFIG_DM_ZERO) += dm-zero.o
obj-$(CONFIG_DM_RAID) += dm-raid.o
obj-$(CONFIG_DM_THIN_PROVISIONING) += dm-thin-pool.o
+obj-$(CONFIG_DM_VERITY) += dm-verity.o
ifeq ($(CONFIG_DM_UEVENT),y)
dm-mod-objs += dm-uevent.o
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index b6e58c7..cc06a1e 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -578,7 +578,7 @@ static void write_endio(struct bio *bio, int error)
struct dm_buffer *b = container_of(bio, struct dm_buffer, bio);
b->write_error = error;
- if (error) {
+ if (unlikely(error)) {
struct dm_bufio_client *c = b->c;
(void)cmpxchg(&c->async_write_error, 0, error);
}
@@ -697,13 +697,20 @@ static void __wait_for_free_buffer(struct dm_bufio_client *c)
dm_bufio_lock(c);
}
+enum new_flag {
+ NF_FRESH = 0,
+ NF_READ = 1,
+ NF_GET = 2,
+ NF_PREFETCH = 3
+};
+
/*
* Allocate a new buffer. If the allocation is not possible, wait until
* some other thread frees a buffer.
*
* May drop the lock and regain it.
*/
-static struct dm_buffer *__alloc_buffer_wait_no_callback(struct dm_bufio_client *c)
+static struct dm_buffer *__alloc_buffer_wait_no_callback(struct dm_bufio_client *c, enum new_flag nf)
{
struct dm_buffer *b;
@@ -726,6 +733,9 @@ static struct dm_buffer *__alloc_buffer_wait_no_callback(struct dm_bufio_client
return b;
}
+ if (nf == NF_PREFETCH)
+ return NULL;
+
if (!list_empty(&c->reserved_buffers)) {
b = list_entry(c->reserved_buffers.next,
struct dm_buffer, lru_list);
@@ -743,9 +753,12 @@ static struct dm_buffer *__alloc_buffer_wait_no_callback(struct dm_bufio_client
}
}
-static struct dm_buffer *__alloc_buffer_wait(struct dm_bufio_client *c)
+static struct dm_buffer *__alloc_buffer_wait(struct dm_bufio_client *c, enum new_flag nf)
{
- struct dm_buffer *b = __alloc_buffer_wait_no_callback(c);
+ struct dm_buffer *b = __alloc_buffer_wait_no_callback(c, nf);
+
+ if (!b)
+ return NULL;
if (c->alloc_callback)
c->alloc_callback(b);
@@ -865,32 +878,23 @@ static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block)
* Getting a buffer
*--------------------------------------------------------------*/
-enum new_flag {
- NF_FRESH = 0,
- NF_READ = 1,
- NF_GET = 2
-};
-
static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
- enum new_flag nf, struct dm_buffer **bp,
- int *need_submit)
+ enum new_flag nf, int *need_submit)
{
struct dm_buffer *b, *new_b = NULL;
*need_submit = 0;
b = __find(c, block);
- if (b) {
- b->hold_count++;
- __relink_lru(b, test_bit(B_DIRTY, &b->state) ||
- test_bit(B_WRITING, &b->state));
- return b;
- }
+ if (b)
+ goto found_buffer;
if (nf == NF_GET)
return NULL;
- new_b = __alloc_buffer_wait(c);
+ new_b = __alloc_buffer_wait(c, nf);
+ if (!new_b)
+ return NULL;
/*
* We've had a period where the mutex was unlocked, so need to
@@ -899,10 +903,7 @@ static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
b = __find(c, block);
if (b) {
__free_buffer_wake(new_b);
- b->hold_count++;
- __relink_lru(b, test_bit(B_DIRTY, &b->state) ||
- test_bit(B_WRITING, &b->state));
- return b;
+ goto found_buffer;
}
__check_watermark(c);
@@ -922,6 +923,24 @@ static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
*need_submit = 1;
return b;
+
+found_buffer:
+ if (nf == NF_PREFETCH)
+ return NULL;
+ /*
+ * Note: it is essential that we don't wait for the buffer to be
+ * read if dm_bufio_get function is used. Both dm_bufio_get and
+ * dm_bufio_prefetch can be used in the driver request routine.
+ * If the user called both dm_bufio_prefetch and dm_bufio_get on
+ * the same buffer, it would deadlock if we waited.
+ */
+ if (nf == NF_GET && unlikely(test_bit(B_READING, &b->state)))
+ return NULL;
+
+ b->hold_count++;
+ __relink_lru(b, test_bit(B_DIRTY, &b->state) ||
+ test_bit(B_WRITING, &b->state));
+ return b;
}
/*
@@ -956,10 +975,10 @@ static void *new_read(struct dm_bufio_client *c, sector_t block,
struct dm_buffer *b;
dm_bufio_lock(c);
- b = __bufio_new(c, block, nf, bp, &need_submit);
+ b = __bufio_new(c, block, nf, &need_submit);
dm_bufio_unlock(c);
- if (!b || IS_ERR(b))
+ if (!b)
return b;
if (need_submit)
@@ -1005,13 +1024,47 @@ void *dm_bufio_new(struct dm_bufio_client *c, sector_t block,
}
EXPORT_SYMBOL_GPL(dm_bufio_new);
+void dm_bufio_prefetch(struct dm_bufio_client *c,
+ sector_t block, unsigned n_blocks)
+{
+ struct blk_plug plug;
+
+ blk_start_plug(&plug);
+ dm_bufio_lock(c);
+
+ for (; n_blocks--; block++) {
+ int need_submit;
+ struct dm_buffer *b;
+ b = __bufio_new(c, block, NF_PREFETCH, &need_submit);
+ if (unlikely(b != NULL)) {
+ dm_bufio_unlock(c);
+
+ if (need_submit)
+ submit_io(b, READ, b->block, read_endio);
+ dm_bufio_release(b);
+
+ dm_bufio_cond_resched();
+
+ if (!n_blocks)
+ goto flush_plug;
+ dm_bufio_lock(c);
+ }
+
+ }
+
+ dm_bufio_unlock(c);
+
+flush_plug:
+ blk_finish_plug(&plug);
+}
+EXPORT_SYMBOL_GPL(dm_bufio_prefetch);
+
void dm_bufio_release(struct dm_buffer *b)
{
struct dm_bufio_client *c = b->c;
dm_bufio_lock(c);
- BUG_ON(test_bit(B_READING, &b->state));
BUG_ON(!b->hold_count);
b->hold_count--;
@@ -1024,6 +1077,7 @@ void dm_bufio_release(struct dm_buffer *b)
* invalid buffer.
*/
if ((b->read_error || b->write_error) &&
+ !test_bit(B_READING, &b->state) &&
!test_bit(B_WRITING, &b->state) &&
!test_bit(B_DIRTY, &b->state)) {
__unlink_buffer(b);
@@ -1041,6 +1095,8 @@ void dm_bufio_mark_buffer_dirty(struct dm_buffer *b)
dm_bufio_lock(c);
+ BUG_ON(test_bit(B_READING, &b->state));
+
if (!test_and_set_bit(B_DIRTY, &b->state))
__relink_lru(b, LIST_DIRTY);
diff --git a/drivers/md/dm-bufio.h b/drivers/md/dm-bufio.h
index 5c4c3a0..b142946 100644
--- a/drivers/md/dm-bufio.h
+++ b/drivers/md/dm-bufio.h
@@ -63,6 +63,14 @@ void *dm_bufio_new(struct dm_bufio_client *c, sector_t block,
struct dm_buffer **bp);
/*
+ * Prefetch the specified blocks to the cache.
+ * The function starts to read the blocks and returns without waiting for
+ * I/O to finish.
+ */
+void dm_bufio_prefetch(struct dm_bufio_client *c,
+ sector_t block, unsigned n_blocks);
+
+/*
* Release a reference obtained with dm_bufio_{read,get,new}. The data
* pointer and dm_buffer pointer is no longer valid after this call.
*/
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index db6b516..3f06df5 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -176,7 +176,6 @@ struct crypt_config {
#define MIN_IOS 16
#define MIN_POOL_PAGES 32
-#define MIN_BIO_PAGES 8
static struct kmem_cache *_crypt_io_pool;
@@ -848,12 +847,11 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size,
}
/*
- * if additional pages cannot be allocated without waiting,
- * return a partially allocated bio, the caller will then try
- * to allocate additional bios while submitting this partial bio
+ * If additional pages cannot be allocated without waiting,
+ * return a partially-allocated bio. The caller will then try
+ * to allocate more bios while submitting this partial bio.
*/
- if (i == (MIN_BIO_PAGES - 1))
- gfp_mask = (gfp_mask | __GFP_NOWARN) & ~__GFP_WAIT;
+ gfp_mask = (gfp_mask | __GFP_NOWARN) & ~__GFP_WAIT;
len = (size > PAGE_SIZE) ? PAGE_SIZE : size;
@@ -1046,16 +1044,14 @@ static void kcryptd_queue_io(struct dm_crypt_io *io)
queue_work(cc->io_queue, &io->work);
}
-static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io,
- int error, int async)
+static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async)
{
struct bio *clone = io->ctx.bio_out;
struct crypt_config *cc = io->target->private;
- if (unlikely(error < 0)) {
+ if (unlikely(io->error < 0)) {
crypt_free_buffer_pages(cc, clone);
bio_put(clone);
- io->error = -EIO;
crypt_dec_pending(io);
return;
}
@@ -1106,12 +1102,16 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
sector += bio_sectors(clone);
crypt_inc_pending(io);
+
r = crypt_convert(cc, &io->ctx);
+ if (r < 0)
+ io->error = -EIO;
+
crypt_finished = atomic_dec_and_test(&io->ctx.pending);
/* Encryption was already finished, submit io now */
if (crypt_finished) {
- kcryptd_crypt_write_io_submit(io, r, 0);
+ kcryptd_crypt_write_io_submit(io, 0);
/*
* If there was an error, do not try next fragments.
@@ -1162,11 +1162,8 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
crypt_dec_pending(io);
}
-static void kcryptd_crypt_read_done(struct dm_crypt_io *io, int error)
+static void kcryptd_crypt_read_done(struct dm_crypt_io *io)
{
- if (unlikely(error < 0))
- io->error = -EIO;
-
crypt_dec_pending(io);
}
@@ -1181,9 +1178,11 @@ static void kcryptd_crypt_read_convert(struct dm_crypt_io *io)
io->sector);
r = crypt_convert(cc, &io->ctx);
+ if (r < 0)
+ io->error = -EIO;
if (atomic_dec_and_test(&io->ctx.pending))
- kcryptd_crypt_read_done(io, r);
+ kcryptd_crypt_read_done(io);
crypt_dec_pending(io);
}
@@ -1204,15 +1203,18 @@ static void kcryptd_async_done(struct crypto_async_request *async_req,
if (!error && cc->iv_gen_ops && cc->iv_gen_ops->post)
error = cc->iv_gen_ops->post(cc, iv_of_dmreq(cc, dmreq), dmreq);
+ if (error < 0)
+ io->error = -EIO;
+
mempool_free(req_of_dmreq(cc, dmreq), cc->req_pool);
if (!atomic_dec_and_test(&ctx->pending))
return;
if (bio_data_dir(io->base_bio) == READ)
- kcryptd_crypt_read_done(io, error);
+ kcryptd_crypt_read_done(io);
else
- kcryptd_crypt_write_io_submit(io, error, 1);
+ kcryptd_crypt_write_io_submit(io, 1);
}
static void kcryptd_crypt(struct work_struct *work)
@@ -1413,6 +1415,7 @@ static int crypt_ctr_cipher(struct dm_target *ti,
char *tmp, *cipher, *chainmode, *ivmode, *ivopts, *keycount;
char *cipher_api = NULL;
int cpu, ret = -EINVAL;
+ char dummy;
/* Convert to crypto api definition? */
if (strchr(cipher_in, '(')) {
@@ -1434,7 +1437,7 @@ static int crypt_ctr_cipher(struct dm_target *ti,
if (!keycount)
cc->tfms_count = 1;
- else if (sscanf(keycount, "%u", &cc->tfms_count) != 1 ||
+ else if (sscanf(keycount, "%u%c", &cc->tfms_count, &dummy) != 1 ||
!is_power_of_2(cc->tfms_count)) {
ti->error = "Bad cipher key count specification";
return -EINVAL;
@@ -1579,6 +1582,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
int ret;
struct dm_arg_set as;
const char *opt_string;
+ char dummy;
static struct dm_arg _args[] = {
{0, 1, "Invalid number of feature args"},
@@ -1636,7 +1640,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
ret = -EINVAL;
- if (sscanf(argv[2], "%llu", &tmpll) != 1) {
+ if (sscanf(argv[2], "%llu%c", &tmpll, &dummy) != 1) {
ti->error = "Invalid iv_offset sector";
goto bad;
}
@@ -1647,7 +1651,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad;
}
- if (sscanf(argv[4], "%llu", &tmpll) != 1) {
+ if (sscanf(argv[4], "%llu%c", &tmpll, &dummy) != 1) {
ti->error = "Invalid device sector";
goto bad;
}
diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c
index f18375d..2dc22dd 100644
--- a/drivers/md/dm-delay.c
+++ b/drivers/md/dm-delay.c
@@ -131,6 +131,7 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
struct delay_c *dc;
unsigned long long tmpll;
+ char dummy;
if (argc != 3 && argc != 6) {
ti->error = "requires exactly 3 or 6 arguments";
@@ -145,13 +146,13 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
dc->reads = dc->writes = 0;
- if (sscanf(argv[1], "%llu", &tmpll) != 1) {
+ if (sscanf(argv[1], "%llu%c", &tmpll, &dummy) != 1) {
ti->error = "Invalid device sector";
goto bad;
}
dc->start_read = tmpll;
- if (sscanf(argv[2], "%u", &dc->read_delay) != 1) {
+ if (sscanf(argv[2], "%u%c", &dc->read_delay, &dummy) != 1) {
ti->error = "Invalid delay";
goto bad;
}
@@ -166,13 +167,13 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
if (argc == 3)
goto out;
- if (sscanf(argv[4], "%llu", &tmpll) != 1) {
+ if (sscanf(argv[4], "%llu%c", &tmpll, &dummy) != 1) {
ti->error = "Invalid write device sector";
goto bad_dev_read;
}
dc->start_write = tmpll;
- if (sscanf(argv[5], "%u", &dc->write_delay) != 1) {
+ if (sscanf(argv[5], "%u%c", &dc->write_delay, &dummy) != 1) {
ti->error = "Invalid write delay";
goto bad_dev_read;
}
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c
index 042e719..aa70f7d 100644
--- a/drivers/md/dm-exception-store.c
+++ b/drivers/md/dm-exception-store.c
@@ -283,7 +283,7 @@ int dm_exception_store_init(void)
return 0;
persistent_fail:
- dm_persistent_snapshot_exit();
+ dm_transient_snapshot_exit();
transient_fail:
return r;
}
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index b280c43..ac49c01 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -160,6 +160,7 @@ static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
unsigned long long tmpll;
struct dm_arg_set as;
const char *devname;
+ char dummy;
as.argc = argc;
as.argv = argv;
@@ -178,7 +179,7 @@ static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
devname = dm_shift_arg(&as);
- if (sscanf(dm_shift_arg(&as), "%llu", &tmpll) != 1) {
+ if (sscanf(dm_shift_arg(&as), "%llu%c", &tmpll, &dummy) != 1) {
ti->error = "Invalid device sector";
goto bad;
}
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 1ce84ed..a1a3e6d 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -880,6 +880,7 @@ static int dev_set_geometry(struct dm_ioctl *param, size_t param_size)
struct hd_geometry geometry;
unsigned long indata[4];
char *geostr = (char *) param + param->data_start;
+ char dummy;
md = find_device(param);
if (!md)
@@ -891,8 +892,8 @@ static int dev_set_geometry(struct dm_ioctl *param, size_t param_size)
goto out;
}
- x = sscanf(geostr, "%lu %lu %lu %lu", indata,
- indata + 1, indata + 2, indata + 3);
+ x = sscanf(geostr, "%lu %lu %lu %lu%c", indata,
+ indata + 1, indata + 2, indata + 3, &dummy);
if (x != 4) {
DMWARN("Unable to interpret geometry settings.");
diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c
index 9728839..3639eea 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -29,6 +29,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
struct linear_c *lc;
unsigned long long tmp;
+ char dummy;
if (argc != 2) {
ti->error = "Invalid argument count";
@@ -41,7 +42,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
return -ENOMEM;
}
- if (sscanf(argv[1], "%llu", &tmp) != 1) {
+ if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1) {
ti->error = "dm-linear: Invalid device sector";
goto bad;
}
diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c
index 3b52bb7..65ebaeb 100644
--- a/drivers/md/dm-log.c
+++ b/drivers/md/dm-log.c
@@ -369,6 +369,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
unsigned int region_count;
size_t bitset_size, buf_size;
int r;
+ char dummy;
if (argc < 1 || argc > 2) {
DMWARN("wrong number of arguments to dirty region log");
@@ -387,7 +388,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
}
}
- if (sscanf(argv[0], "%u", &region_size) != 1 ||
+ if (sscanf(argv[0], "%u%c", &region_size, &dummy) != 1 ||
!_check_region_size(ti, region_size)) {
DMWARN("invalid region size %s", argv[0]);
return -EINVAL;
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 801d92d..922a338 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -226,6 +226,27 @@ static void free_multipath(struct multipath *m)
kfree(m);
}
+static int set_mapinfo(struct multipath *m, union map_info *info)
+{
+ struct dm_mpath_io *mpio;
+
+ mpio = mempool_alloc(m->mpio_pool, GFP_ATOMIC);
+ if (!mpio)
+ return -ENOMEM;
+
+ memset(mpio, 0, sizeof(*mpio));
+ info->ptr = mpio;
+
+ return 0;
+}
+
+static void clear_mapinfo(struct multipath *m, union map_info *info)
+{
+ struct dm_mpath_io *mpio = info->ptr;
+
+ info->ptr = NULL;
+ mempool_free(mpio, m->mpio_pool);
+}
/*-----------------------------------------------
* Path selection
@@ -341,13 +362,14 @@ static int __must_push_back(struct multipath *m)
}
static int map_io(struct multipath *m, struct request *clone,
- struct dm_mpath_io *mpio, unsigned was_queued)
+ union map_info *map_context, unsigned was_queued)
{
int r = DM_MAPIO_REMAPPED;
size_t nr_bytes = blk_rq_bytes(clone);
unsigned long flags;
struct pgpath *pgpath;
struct block_device *bdev;
+ struct dm_mpath_io *mpio = map_context->ptr;
spin_lock_irqsave(&m->lock, flags);
@@ -423,7 +445,6 @@ static void dispatch_queued_ios(struct multipath *m)
{
int r;
unsigned long flags;
- struct dm_mpath_io *mpio;
union map_info *info;
struct request *clone, *n;
LIST_HEAD(cl);
@@ -436,16 +457,15 @@ static void dispatch_queued_ios(struct multipath *m)
list_del_init(&clone->queuelist);
info = dm_get_rq_mapinfo(clone);
- mpio = info->ptr;
- r = map_io(m, clone, mpio, 1);
+ r = map_io(m, clone, info, 1);
if (r < 0) {
- mempool_free(mpio, m->mpio_pool);
+ clear_mapinfo(m, info);
dm_kill_unmapped_request(clone, r);
} else if (r == DM_MAPIO_REMAPPED)
dm_dispatch_request(clone);
else if (r == DM_MAPIO_REQUEUE) {
- mempool_free(mpio, m->mpio_pool);
+ clear_mapinfo(m, info);
dm_requeue_unmapped_request(clone);
}
}
@@ -908,20 +928,16 @@ static int multipath_map(struct dm_target *ti, struct request *clone,
union map_info *map_context)
{
int r;
- struct dm_mpath_io *mpio;
struct multipath *m = (struct multipath *) ti->private;
- mpio = mempool_alloc(m->mpio_pool, GFP_ATOMIC);
- if (!mpio)
+ if (set_mapinfo(m, map_context) < 0)
/* ENOMEM, requeue */
return DM_MAPIO_REQUEUE;
- memset(mpio, 0, sizeof(*mpio));
- map_context->ptr = mpio;
clone->cmd_flags |= REQ_FAILFAST_TRANSPORT;
- r = map_io(m, clone, mpio, 0);
+ r = map_io(m, clone, map_context, 0);
if (r < 0 || r == DM_MAPIO_REQUEUE)
- mempool_free(mpio, m->mpio_pool);
+ clear_mapinfo(m, map_context);
return r;
}
@@ -1054,8 +1070,9 @@ static int switch_pg_num(struct multipath *m, const char *pgstr)
struct priority_group *pg;
unsigned pgnum;
unsigned long flags;
+ char dummy;
- if (!pgstr || (sscanf(pgstr, "%u", &pgnum) != 1) || !pgnum ||
+ if (!pgstr || (sscanf(pgstr, "%u%c", &pgnum, &dummy) != 1) || !pgnum ||
(pgnum > m->nr_priority_groups)) {
DMWARN("invalid PG number supplied to switch_pg_num");
return -EINVAL;
@@ -1085,8 +1102,9 @@ static int bypass_pg_num(struct multipath *m, const char *pgstr, int bypassed)
{
struct priority_group *pg;
unsigned pgnum;
+ char dummy;
- if (!pgstr || (sscanf(pgstr, "%u", &pgnum) != 1) || !pgnum ||
+ if (!pgstr || (sscanf(pgstr, "%u%c", &pgnum, &dummy) != 1) || !pgnum ||
(pgnum > m->nr_priority_groups)) {
DMWARN("invalid PG number supplied to bypass_pg");
return -EINVAL;
@@ -1261,13 +1279,15 @@ static int multipath_end_io(struct dm_target *ti, struct request *clone,
struct path_selector *ps;
int r;
+ BUG_ON(!mpio);
+
r = do_end_io(m, clone, error, mpio);
if (pgpath) {
ps = &pgpath->pg->ps;
if (ps->type->end_io)
ps->type->end_io(ps, &pgpath->path, mpio->nr_bytes);
}
- mempool_free(mpio, m->mpio_pool);
+ clear_mapinfo(m, map_context);
return r;
}
diff --git a/drivers/md/dm-queue-length.c b/drivers/md/dm-queue-length.c
index 03a837a..3941fae 100644
--- a/drivers/md/dm-queue-length.c
+++ b/drivers/md/dm-queue-length.c
@@ -112,6 +112,7 @@ static int ql_add_path(struct path_selector *ps, struct dm_path *path,
struct selector *s = ps->context;
struct path_info *pi;
unsigned repeat_count = QL_MIN_IO;
+ char dummy;
/*
* Arguments: [<repeat_count>]
@@ -123,7 +124,7 @@ static int ql_add_path(struct path_selector *ps, struct dm_path *path,
return -EINVAL;
}
- if ((argc == 1) && (sscanf(argv[0], "%u", &repeat_count) != 1)) {
+ if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) {
*error = "queue-length ps: invalid repeat count";
return -EINVAL;
}
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index c5a875d..b0ba524 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -604,7 +604,9 @@ static int read_disk_sb(struct md_rdev *rdev, int size)
return 0;
if (!sync_page_io(rdev, 0, size, rdev->sb_page, READ, 1)) {
- DMERR("Failed to read device superblock");
+ DMERR("Failed to read superblock of device at position %d",
+ rdev->raid_disk);
+ set_bit(Faulty, &rdev->flags);
return -EINVAL;
}
@@ -855,9 +857,25 @@ static int super_validate(struct mddev *mddev, struct md_rdev *rdev)
static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
{
int ret;
+ unsigned redundancy = 0;
+ struct raid_dev *dev;
struct md_rdev *rdev, *freshest;
struct mddev *mddev = &rs->md;
+ switch (rs->raid_type->level) {
+ case 1:
+ redundancy = rs->md.raid_disks - 1;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ redundancy = rs->raid_type->parity_devs;
+ break;
+ default:
+ ti->error = "Unknown RAID type";
+ return -EINVAL;
+ }
+
freshest = NULL;
rdev_for_each(rdev, mddev) {
if (!rdev->meta_bdev)
@@ -872,6 +890,37 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
case 0:
break;
default:
+ dev = container_of(rdev, struct raid_dev, rdev);
+ if (redundancy--) {
+ if (dev->meta_dev)
+ dm_put_device(ti, dev->meta_dev);
+
+ dev->meta_dev = NULL;
+ rdev->meta_bdev = NULL;
+
+ if (rdev->sb_page)
+ put_page(rdev->sb_page);
+
+ rdev->sb_page = NULL;
+
+ rdev->sb_loaded = 0;
+
+ /*
+ * We might be able to salvage the data device
+ * even though the meta device has failed. For
+ * now, we behave as though '- -' had been
+ * set for this device in the table.
+ */
+ if (dev->data_dev)
+ dm_put_device(ti, dev->data_dev);
+
+ dev->data_dev = NULL;
+ rdev->bdev = NULL;
+
+ list_del(&rdev->same_set);
+
+ continue;
+ }
ti->error = "Failed to load superblock";
return ret;
}
@@ -1214,7 +1263,7 @@ static void raid_resume(struct dm_target *ti)
static struct target_type raid_target = {
.name = "raid",
- .version = {1, 1, 0},
+ .version = {1, 2, 0},
.module = THIS_MODULE,
.ctr = raid_ctr,
.dtr = raid_dtr,
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index 9bfd057..d039de8 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -924,8 +924,9 @@ static int get_mirror(struct mirror_set *ms, struct dm_target *ti,
unsigned int mirror, char **argv)
{
unsigned long long offset;
+ char dummy;
- if (sscanf(argv[1], "%llu", &offset) != 1) {
+ if (sscanf(argv[1], "%llu%c", &offset, &dummy) != 1) {
ti->error = "Invalid offset";
return -EINVAL;
}
@@ -953,13 +954,14 @@ static struct dm_dirty_log *create_dirty_log(struct dm_target *ti,
{
unsigned param_count;
struct dm_dirty_log *dl;
+ char dummy;
if (argc < 2) {
ti->error = "Insufficient mirror log arguments";
return NULL;
}
- if (sscanf(argv[1], "%u", &param_count) != 1) {
+ if (sscanf(argv[1], "%u%c", &param_count, &dummy) != 1) {
ti->error = "Invalid mirror log argument count";
return NULL;
}
@@ -986,13 +988,14 @@ static int parse_features(struct mirror_set *ms, unsigned argc, char **argv,
{
unsigned num_features;
struct dm_target *ti = ms->ti;
+ char dummy;
*args_used = 0;
if (!argc)
return 0;
- if (sscanf(argv[0], "%u", &num_features) != 1) {
+ if (sscanf(argv[0], "%u%c", &num_features, &dummy) != 1) {
ti->error = "Invalid number of features";
return -EINVAL;
}
@@ -1036,6 +1039,7 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
unsigned int nr_mirrors, m, args_used;
struct mirror_set *ms;
struct dm_dirty_log *dl;
+ char dummy;
dl = create_dirty_log(ti, argc, argv, &args_used);
if (!dl)
@@ -1044,7 +1048,7 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
argv += args_used;
argc -= args_used;
- if (!argc || sscanf(argv[0], "%u", &nr_mirrors) != 1 ||
+ if (!argc || sscanf(argv[0], "%u%c", &nr_mirrors, &dummy) != 1 ||
nr_mirrors < 2 || nr_mirrors > DM_KCOPYD_MAX_REGIONS + 1) {
ti->error = "Invalid number of mirrors";
dm_dirty_log_destroy(dl);
diff --git a/drivers/md/dm-round-robin.c b/drivers/md/dm-round-robin.c
index 27f1d42..6ab1192 100644
--- a/drivers/md/dm-round-robin.c
+++ b/drivers/md/dm-round-robin.c
@@ -114,6 +114,7 @@ static int rr_add_path(struct path_selector *ps, struct dm_path *path,
struct selector *s = (struct selector *) ps->context;
struct path_info *pi;
unsigned repeat_count = RR_MIN_IO;
+ char dummy;
if (argc > 1) {
*error = "round-robin ps: incorrect number of arguments";
@@ -121,7 +122,7 @@ static int rr_add_path(struct path_selector *ps, struct dm_path *path,
}
/* First path argument is number of I/Os before switching path */
- if ((argc == 1) && (sscanf(argv[0], "%u", &repeat_count) != 1)) {
+ if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) {
*error = "round-robin ps: invalid repeat count";
return -EINVAL;
}
diff --git a/drivers/md/dm-service-time.c b/drivers/md/dm-service-time.c
index 59883bd..9df8f6b 100644
--- a/drivers/md/dm-service-time.c
+++ b/drivers/md/dm-service-time.c
@@ -110,6 +110,7 @@ static int st_add_path(struct path_selector *ps, struct dm_path *path,
struct path_info *pi;
unsigned repeat_count = ST_MIN_IO;
unsigned relative_throughput = 1;
+ char dummy;
/*
* Arguments: [<repeat_count> [<relative_throughput>]]
@@ -128,13 +129,13 @@ static int st_add_path(struct path_selector *ps, struct dm_path *path,
return -EINVAL;
}
- if (argc && (sscanf(argv[0], "%u", &repeat_count) != 1)) {
+ if (argc && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) {
*error = "service-time ps: invalid repeat count";
return -EINVAL;
}
if ((argc == 2) &&
- (sscanf(argv[1], "%u", &relative_throughput) != 1 ||
+ (sscanf(argv[1], "%u%c", &relative_throughput, &dummy) != 1 ||
relative_throughput > ST_MAX_RELATIVE_THROUGHPUT)) {
*error = "service-time ps: invalid relative_throughput value";
return -EINVAL;
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index 3d80cf0..35c94ff 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -75,8 +75,9 @@ static int get_stripe(struct dm_target *ti, struct stripe_c *sc,
unsigned int stripe, char **argv)
{
unsigned long long start;
+ char dummy;
- if (sscanf(argv[1], "%llu", &start) != 1)
+ if (sscanf(argv[1], "%llu%c", &start, &dummy) != 1)
return -EINVAL;
if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table),
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 63cc542..2e227fb 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -268,8 +268,7 @@ void dm_table_destroy(struct dm_table *t)
vfree(t->highs);
/* free the device list */
- if (t->devices.next != &t->devices)
- free_devices(&t->devices);
+ free_devices(&t->devices);
dm_free_md_mempools(t->mempools);
@@ -464,10 +463,11 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
struct dm_dev_internal *dd;
unsigned int major, minor;
struct dm_table *t = ti->table;
+ char dummy;
BUG_ON(!t);
- if (sscanf(path, "%u:%u", &major, &minor) == 2) {
+ if (sscanf(path, "%u:%u%c", &major, &minor, &dummy) == 2) {
/* Extract the major/minor numbers */
dev = MKDEV(major, minor);
if (MAJOR(dev) != major || MINOR(dev) != minor)
@@ -842,9 +842,10 @@ static int validate_next_arg(struct dm_arg *arg, struct dm_arg_set *arg_set,
unsigned *value, char **error, unsigned grouped)
{
const char *arg_str = dm_shift_arg(arg_set);
+ char dummy;
if (!arg_str ||
- (sscanf(arg_str, "%u", value) != 1) ||
+ (sscanf(arg_str, "%u%c", value, &dummy) != 1) ||
(*value < arg->min) ||
(*value > arg->max) ||
(grouped && arg_set->argc < *value)) {
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index 237571a..737d388 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -614,7 +614,7 @@ static int __commit_transaction(struct dm_pool_metadata *pmd)
if (r < 0)
goto out;
- r = dm_sm_root_size(pmd->metadata_sm, &data_len);
+ r = dm_sm_root_size(pmd->data_sm, &data_len);
if (r < 0)
goto out;
@@ -713,6 +713,9 @@ struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
if (r)
goto bad;
+ if (bdev_size > THIN_METADATA_MAX_SECTORS)
+ bdev_size = THIN_METADATA_MAX_SECTORS;
+
disk_super = dm_block_data(sblock);
disk_super->magic = cpu_to_le64(THIN_SUPERBLOCK_MAGIC);
disk_super->version = cpu_to_le32(THIN_VERSION);
diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h
index 859c168..ed4725e 100644
--- a/drivers/md/dm-thin-metadata.h
+++ b/drivers/md/dm-thin-metadata.h
@@ -11,6 +11,19 @@
#define THIN_METADATA_BLOCK_SIZE 4096
+/*
+ * The metadata device is currently limited in size.
+ *
+ * We have one block of index, which can hold 255 index entries. Each
+ * index entry contains allocation info about 16k metadata blocks.
+ */
+#define THIN_METADATA_MAX_SECTORS (255 * (1 << 14) * (THIN_METADATA_BLOCK_SIZE / (1 << SECTOR_SHIFT)))
+
+/*
+ * A metadata device larger than 16GB triggers a warning.
+ */
+#define THIN_METADATA_MAX_SECTORS_WARNING (16 * (1024 * 1024 * 1024 >> SECTOR_SHIFT))
+
/*----------------------------------------------------------------*/
struct dm_pool_metadata;
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index c308757..213ae32 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -23,6 +23,7 @@
#define DEFERRED_SET_SIZE 64
#define MAPPING_POOL_SIZE 1024
#define PRISON_CELLS 1024
+#define COMMIT_PERIOD HZ
/*
* The block size of the device holding pool data must be
@@ -32,16 +33,6 @@
#define DATA_DEV_BLOCK_SIZE_MAX_SECTORS (1024 * 1024 * 1024 >> SECTOR_SHIFT)
/*
- * The metadata device is currently limited in size. The limitation is
- * checked lower down in dm-space-map-metadata, but we also check it here
- * so we can fail early.
- *
- * We have one block of index, which can hold 255 index entries. Each
- * index entry contains allocation info about 16k metadata blocks.
- */
-#define METADATA_DEV_MAX_SECTORS (255 * (1 << 14) * (THIN_METADATA_BLOCK_SIZE / (1 << SECTOR_SHIFT)))
-
-/*
* Device id is restricted to 24 bits.
*/
#define MAX_DEV_ID ((1 << 24) - 1)
@@ -72,7 +63,7 @@
* missed out if the io covers the block. (schedule_copy).
*
* iv) insert the new mapping into the origin's btree
- * (process_prepared_mappings). This act of inserting breaks some
+ * (process_prepared_mapping). This act of inserting breaks some
* sharing of btree nodes between the two devices. Breaking sharing only
* effects the btree of that specific device. Btrees for the other
* devices that share the block never change. The btree for the origin
@@ -124,7 +115,7 @@ struct cell {
struct hlist_node list;
struct bio_prison *prison;
struct cell_key key;
- unsigned count;
+ struct bio *holder;
struct bio_list bios;
};
@@ -220,54 +211,59 @@ static struct cell *__search_bucket(struct hlist_head *bucket,
* This may block if a new cell needs allocating. You must ensure that
* cells will be unlocked even if the calling thread is blocked.
*
- * Returns the number of entries in the cell prior to the new addition
- * or < 0 on failure.
+ * Returns 1 if the cell was already held, 0 if @inmate is the new holder.
*/
static int bio_detain(struct bio_prison *prison, struct cell_key *key,
struct bio *inmate, struct cell **ref)
{
- int r;
+ int r = 1;
unsigned long flags;
uint32_t hash = hash_key(prison, key);
- struct cell *uninitialized_var(cell), *cell2 = NULL;
+ struct cell *cell, *cell2;
BUG_ON(hash > prison->nr_buckets);
spin_lock_irqsave(&prison->lock, flags);
+
cell = __search_bucket(prison->cells + hash, key);
+ if (cell) {
+ bio_list_add(&cell->bios, inmate);
+ goto out;
+ }
- if (!cell) {
- /*
- * Allocate a new cell
- */
- spin_unlock_irqrestore(&prison->lock, flags);
- cell2 = mempool_alloc(prison->cell_pool, GFP_NOIO);
- spin_lock_irqsave(&prison->lock, flags);
+ /*
+ * Allocate a new cell
+ */
+ spin_unlock_irqrestore(&prison->lock, flags);
+ cell2 = mempool_alloc(prison->cell_pool, GFP_NOIO);
+ spin_lock_irqsave(&prison->lock, flags);
- /*
- * We've been unlocked, so we have to double check that
- * nobody else has inserted this cell in the meantime.
- */
- cell = __search_bucket(prison->cells + hash, key);
+ /*
+ * We've been unlocked, so we have to double check that
+ * nobody else has inserted this cell in the meantime.
+ */
+ cell = __search_bucket(prison->cells + hash, key);
+ if (cell) {
+ mempool_free(cell2, prison->cell_pool);
+ bio_list_add(&cell->bios, inmate);
+ goto out;
+ }
- if (!cell) {
- cell = cell2;
- cell2 = NULL;
+ /*
+ * Use new cell.
+ */
+ cell = cell2;
- cell->prison = prison;
- memcpy(&cell->key, key, sizeof(cell->key));
- cell->count = 0;
- bio_list_init(&cell->bios);
- hlist_add_head(&cell->list, prison->cells + hash);
- }
- }
+ cell->prison = prison;
+ memcpy(&cell->key, key, sizeof(cell->key));
+ cell->holder = inmate;
+ bio_list_init(&cell->bios);
+ hlist_add_head(&cell->list, prison->cells + hash);
- r = cell->count++;
- bio_list_add(&cell->bios, inmate);
- spin_unlock_irqrestore(&prison->lock, flags);
+ r = 0;
- if (cell2)
- mempool_free(cell2, prison->cell_pool);
+out:
+ spin_unlock_irqrestore(&prison->lock, flags);
*ref = cell;
@@ -283,8 +279,8 @@ static void __cell_release(struct cell *cell, struct bio_list *inmates)
hlist_del(&cell->list);
- if (inmates)
- bio_list_merge(inmates, &cell->bios);
+ bio_list_add(inmates, cell->holder);
+ bio_list_merge(inmates, &cell->bios);
mempool_free(cell, prison->cell_pool);
}
@@ -305,22 +301,44 @@ static void cell_release(struct cell *cell, struct bio_list *bios)
* bio may be in the cell. This function releases the cell, and also does
* a sanity check.
*/
+static void __cell_release_singleton(struct cell *cell, struct bio *bio)
+{
+ hlist_del(&cell->list);
+ BUG_ON(cell->holder != bio);
+ BUG_ON(!bio_list_empty(&cell->bios));
+}
+
static void cell_release_singleton(struct cell *cell, struct bio *bio)
{
- struct bio_prison *prison = cell->prison;
- struct bio_list bios;
- struct bio *b;
unsigned long flags;
-
- bio_list_init(&bios);
+ struct bio_prison *prison = cell->prison;
spin_lock_irqsave(&prison->lock, flags);
- __cell_release(cell, &bios);
+ __cell_release_singleton(cell, bio);
spin_unlock_irqrestore(&prison->lock, flags);
+}
+
+/*
+ * Sometimes we don't want the holder, just the additional bios.
+ */
+static void __cell_release_no_holder(struct cell *cell, struct bio_list *inmates)
+{
+ struct bio_prison *prison = cell->prison;
+
+ hlist_del(&cell->list);
+ bio_list_merge(inmates, &cell->bios);
- b = bio_list_pop(&bios);
- BUG_ON(b != bio);
- BUG_ON(!bio_list_empty(&bios));
+ mempool_free(cell, prison->cell_pool);
+}
+
+static void cell_release_no_holder(struct cell *cell, struct bio_list *inmates)
+{
+ unsigned long flags;
+ struct bio_prison *prison = cell->prison;
+
+ spin_lock_irqsave(&prison->lock, flags);
+ __cell_release_no_holder(cell, inmates);
+ spin_unlock_irqrestore(&prison->lock, flags);
}
static void cell_error(struct cell *cell)
@@ -471,6 +489,13 @@ static void build_virtual_key(struct dm_thin_device *td, dm_block_t b,
* devices.
*/
struct new_mapping;
+
+struct pool_features {
+ unsigned zero_new_blocks:1;
+ unsigned discard_enabled:1;
+ unsigned discard_passdown:1;
+};
+
struct pool {
struct list_head list;
struct dm_target *ti; /* Only set if a pool target is bound */
@@ -484,7 +509,7 @@ struct pool {
dm_block_t offset_mask;
dm_block_t low_water_blocks;
- unsigned zero_new_blocks:1;
+ struct pool_features pf;
unsigned low_water_triggered:1; /* A dm event has been sent */
unsigned no_free_space:1; /* A -ENOSPC warning has been issued */
@@ -493,17 +518,21 @@ struct pool {
struct workqueue_struct *wq;
struct work_struct worker;
+ struct delayed_work waker;
unsigned ref_count;
+ unsigned long last_commit_jiffies;
spinlock_t lock;
struct bio_list deferred_bios;
struct bio_list deferred_flush_bios;
struct list_head prepared_mappings;
+ struct list_head prepared_discards;
struct bio_list retry_on_resume_list;
- struct deferred_set ds; /* FIXME: move to thin_c */
+ struct deferred_set shared_read_ds;
+ struct deferred_set all_io_ds;
struct new_mapping *next_mapping;
mempool_t *mapping_pool;
@@ -521,7 +550,7 @@ struct pool_c {
struct dm_target_callbacks callbacks;
dm_block_t low_water_blocks;
- unsigned zero_new_blocks:1;
+ struct pool_features pf;
};
/*
@@ -529,6 +558,7 @@ struct pool_c {
*/
struct thin_c {
struct dm_dev *pool_dev;
+ struct dm_dev *origin_dev;
dm_thin_id dev_id;
struct pool *pool;
@@ -597,6 +627,13 @@ static struct pool *__pool_table_lookup_metadata_dev(struct block_device *md_dev
/*----------------------------------------------------------------*/
+struct endio_hook {
+ struct thin_c *tc;
+ struct deferred_entry *shared_read_entry;
+ struct deferred_entry *all_io_entry;
+ struct new_mapping *overwrite_mapping;
+};
+
static void __requeue_bio_list(struct thin_c *tc, struct bio_list *master)
{
struct bio *bio;
@@ -607,7 +644,8 @@ static void __requeue_bio_list(struct thin_c *tc, struct bio_list *master)
bio_list_init(master);
while ((bio = bio_list_pop(&bios))) {
- if (dm_get_mapinfo(bio)->ptr == tc)
+ struct endio_hook *h = dm_get_mapinfo(bio)->ptr;
+ if (h->tc == tc)
bio_endio(bio, DM_ENDIO_REQUEUE);
else
bio_list_add(master, bio);
@@ -646,14 +684,16 @@ static void remap(struct thin_c *tc, struct bio *bio, dm_block_t block)
(bio->bi_sector & pool->offset_mask);
}
-static void remap_and_issue(struct thin_c *tc, struct bio *bio,
- dm_block_t block)
+static void remap_to_origin(struct thin_c *tc, struct bio *bio)
+{
+ bio->bi_bdev = tc->origin_dev->bdev;
+}
+
+static void issue(struct thin_c *tc, struct bio *bio)
{
struct pool *pool = tc->pool;
unsigned long flags;
- remap(tc, bio, block);
-
/*
* Batch together any FUA/FLUSH bios we find and then issue
* a single commit for them in process_deferred_bios().
@@ -666,6 +706,19 @@ static void remap_and_issue(struct thin_c *tc, struct bio *bio,
generic_make_request(bio);
}
+static void remap_to_origin_and_issue(struct thin_c *tc, struct bio *bio)
+{
+ remap_to_origin(tc, bio);
+ issue(tc, bio);
+}
+
+static void remap_and_issue(struct thin_c *tc, struct bio *bio,
+ dm_block_t block)
+{
+ remap(tc, bio, block);
+ issue(tc, bio);
+}
+
/*
* wake_worker() is used when new work is queued and when pool_resume is
* ready to continue deferred IO processing.
@@ -680,21 +733,17 @@ static void wake_worker(struct pool *pool)
/*
* Bio endio functions.
*/
-struct endio_hook {
- struct thin_c *tc;
- bio_end_io_t *saved_bi_end_io;
- struct deferred_entry *entry;
-};
-
struct new_mapping {
struct list_head list;
- int prepared;
+ unsigned quiesced:1;
+ unsigned prepared:1;
+ unsigned pass_discard:1;
struct thin_c *tc;
dm_block_t virt_block;
dm_block_t data_block;
- struct cell *cell;
+ struct cell *cell, *cell2;
int err;
/*
@@ -711,7 +760,7 @@ static void __maybe_add_mapping(struct new_mapping *m)
{
struct pool *pool = m->tc->pool;
- if (list_empty(&m->list) && m->prepared) {
+ if (m->quiesced && m->prepared) {
list_add(&m->list, &pool->prepared_mappings);
wake_worker(pool);
}
@@ -734,7 +783,8 @@ static void copy_complete(int read_err, unsigned long write_err, void *context)
static void overwrite_endio(struct bio *bio, int err)
{
unsigned long flags;
- struct new_mapping *m = dm_get_mapinfo(bio)->ptr;
+ struct endio_hook *h = dm_get_mapinfo(bio)->ptr;
+ struct new_mapping *m = h->overwrite_mapping;
struct pool *pool = m->tc->pool;
m->err = err;
@@ -745,31 +795,6 @@ static void overwrite_endio(struct bio *bio, int err)
spin_unlock_irqrestore(&pool->lock, flags);
}
-static void shared_read_endio(struct bio *bio, int err)
-{
- struct list_head mappings;
- struct new_mapping *m, *tmp;
- struct endio_hook *h = dm_get_mapinfo(bio)->ptr;
- unsigned long flags;
- struct pool *pool = h->tc->pool;
-
- bio->bi_end_io = h->saved_bi_end_io;
- bio_endio(bio, err);
-
- INIT_LIST_HEAD(&mappings);
- ds_dec(h->entry, &mappings);
-
- spin_lock_irqsave(&pool->lock, flags);
- list_for_each_entry_safe(m, tmp, &mappings, list) {
- list_del(&m->list);
- INIT_LIST_HEAD(&m->list);
- __maybe_add_mapping(m);
- }
- spin_unlock_irqrestore(&pool->lock, flags);
-
- mempool_free(h, pool->endio_hook_pool);
-}
-
/*----------------------------------------------------------------*/
/*
@@ -800,21 +825,16 @@ static void cell_defer(struct thin_c *tc, struct cell *cell,
* Same as cell_defer above, except it omits one particular detainee,
* a write bio that covers the block and has already been processed.
*/
-static void cell_defer_except(struct thin_c *tc, struct cell *cell,
- struct bio *exception)
+static void cell_defer_except(struct thin_c *tc, struct cell *cell)
{
struct bio_list bios;
- struct bio *bio;
struct pool *pool = tc->pool;
unsigned long flags;
bio_list_init(&bios);
- cell_release(cell, &bios);
spin_lock_irqsave(&pool->lock, flags);
- while ((bio = bio_list_pop(&bios)))
- if (bio != exception)
- bio_list_add(&pool->deferred_bios, bio);
+ cell_release_no_holder(cell, &pool->deferred_bios);
spin_unlock_irqrestore(&pool->lock, flags);
wake_worker(pool);
@@ -854,7 +874,7 @@ static void process_prepared_mapping(struct new_mapping *m)
* the bios in the cell.
*/
if (bio) {
- cell_defer_except(tc, m->cell, bio);
+ cell_defer_except(tc, m->cell);
bio_endio(bio, 0);
} else
cell_defer(tc, m->cell, m->data_block);
@@ -863,7 +883,30 @@ static void process_prepared_mapping(struct new_mapping *m)
mempool_free(m, tc->pool->mapping_pool);
}
-static void process_prepared_mappings(struct pool *pool)
+static void process_prepared_discard(struct new_mapping *m)
+{
+ int r;
+ struct thin_c *tc = m->tc;
+
+ r = dm_thin_remove_block(tc->td, m->virt_block);
+ if (r)
+ DMERR("dm_thin_remove_block() failed");
+
+ /*
+ * Pass the discard down to the underlying device?
+ */
+ if (m->pass_discard)
+ remap_and_issue(tc, m->bio, m->data_block);
+ else
+ bio_endio(m->bio, 0);
+
+ cell_defer_except(tc, m->cell);
+ cell_defer_except(tc, m->cell2);
+ mempool_free(m, tc->pool->mapping_pool);
+}
+
+static void process_prepared(struct pool *pool, struct list_head *head,
+ void (*fn)(struct new_mapping *))
{
unsigned long flags;
struct list_head maps;
@@ -871,21 +914,27 @@ static void process_prepared_mappings(struct pool *pool)
INIT_LIST_HEAD(&maps);
spin_lock_irqsave(&pool->lock, flags);
- list_splice_init(&pool->prepared_mappings, &maps);
+ list_splice_init(head, &maps);
spin_unlock_irqrestore(&pool->lock, flags);
list_for_each_entry_safe(m, tmp, &maps, list)
- process_prepared_mapping(m);
+ fn(m);
}
/*
* Deferred bio jobs.
*/
-static int io_overwrites_block(struct pool *pool, struct bio *bio)
+static int io_overlaps_block(struct pool *pool, struct bio *bio)
{
- return ((bio_data_dir(bio) == WRITE) &&
- !(bio->bi_sector & pool->offset_mask)) &&
+ return !(bio->bi_sector & pool->offset_mask) &&
(bio->bi_size == (pool->sectors_per_block << SECTOR_SHIFT));
+
+}
+
+static int io_overwrites_block(struct pool *pool, struct bio *bio)
+{
+ return (bio_data_dir(bio) == WRITE) &&
+ io_overlaps_block(pool, bio);
}
static void save_and_set_endio(struct bio *bio, bio_end_io_t **save,
@@ -917,7 +966,8 @@ static struct new_mapping *get_next_mapping(struct pool *pool)
}
static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
- dm_block_t data_origin, dm_block_t data_dest,
+ struct dm_dev *origin, dm_block_t data_origin,
+ dm_block_t data_dest,
struct cell *cell, struct bio *bio)
{
int r;
@@ -925,6 +975,7 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
struct new_mapping *m = get_next_mapping(pool);
INIT_LIST_HEAD(&m->list);
+ m->quiesced = 0;
m->prepared = 0;
m->tc = tc;
m->virt_block = virt_block;
@@ -933,7 +984,8 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
m->err = 0;
m->bio = NULL;
- ds_add_work(&pool->ds, &m->list);
+ if (!ds_add_work(&pool->shared_read_ds, &m->list))
+ m->quiesced = 1;
/*
* IO to pool_dev remaps to the pool target's data_dev.
@@ -942,14 +994,15 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
* bio immediately. Otherwise we use kcopyd to clone the data first.
*/
if (io_overwrites_block(pool, bio)) {
+ struct endio_hook *h = dm_get_mapinfo(bio)->ptr;
+ h->overwrite_mapping = m;
m->bio = bio;
save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio);
- dm_get_mapinfo(bio)->ptr = m;
remap_and_issue(tc, bio, data_dest);
} else {
struct dm_io_region from, to;
- from.bdev = tc->pool_dev->bdev;
+ from.bdev = origin->bdev;
from.sector = data_origin * pool->sectors_per_block;
from.count = pool->sectors_per_block;
@@ -967,6 +1020,22 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
}
}
+static void schedule_internal_copy(struct thin_c *tc, dm_block_t virt_block,
+ dm_block_t data_origin, dm_block_t data_dest,
+ struct cell *cell, struct bio *bio)
+{
+ schedule_copy(tc, virt_block, tc->pool_dev,
+ data_origin, data_dest, cell, bio);
+}
+
+static void schedule_external_copy(struct thin_c *tc, dm_block_t virt_block,
+ dm_block_t data_dest,
+ struct cell *cell, struct bio *bio)
+{
+ schedule_copy(tc, virt_block, tc->origin_dev,
+ virt_block, data_dest, cell, bio);
+}
+
static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
dm_block_t data_block, struct cell *cell,
struct bio *bio)
@@ -975,6 +1044,7 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
struct new_mapping *m = get_next_mapping(pool);
INIT_LIST_HEAD(&m->list);
+ m->quiesced = 1;
m->prepared = 0;
m->tc = tc;
m->virt_block = virt_block;
@@ -988,13 +1058,14 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
* zeroing pre-existing data, we can issue the bio immediately.
* Otherwise we use kcopyd to zero the data first.
*/
- if (!pool->zero_new_blocks)
+ if (!pool->pf.zero_new_blocks)
process_prepared_mapping(m);
else if (io_overwrites_block(pool, bio)) {
+ struct endio_hook *h = dm_get_mapinfo(bio)->ptr;
+ h->overwrite_mapping = m;
m->bio = bio;
save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio);
- dm_get_mapinfo(bio)->ptr = m;
remap_and_issue(tc, bio, data_block);
} else {
@@ -1081,7 +1152,8 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result)
*/
static void retry_on_resume(struct bio *bio)
{
- struct thin_c *tc = dm_get_mapinfo(bio)->ptr;
+ struct endio_hook *h = dm_get_mapinfo(bio)->ptr;
+ struct thin_c *tc = h->tc;
struct pool *pool = tc->pool;
unsigned long flags;
@@ -1102,6 +1174,86 @@ static void no_space(struct cell *cell)
retry_on_resume(bio);
}
+static void process_discard(struct thin_c *tc, struct bio *bio)
+{
+ int r;
+ struct pool *pool = tc->pool;
+ struct cell *cell, *cell2;
+ struct cell_key key, key2;
+ dm_block_t block = get_bio_block(tc, bio);
+ struct dm_thin_lookup_result lookup_result;
+ struct new_mapping *m;
+
+ build_virtual_key(tc->td, block, &key);
+ if (bio_detain(tc->pool->prison, &key, bio, &cell))
+ return;
+
+ r = dm_thin_find_block(tc->td, block, 1, &lookup_result);
+ switch (r) {
+ case 0:
+ /*
+ * Check nobody is fiddling with this pool block. This can
+ * happen if someone's in the process of breaking sharing
+ * on this block.
+ */
+ build_data_key(tc->td, lookup_result.block, &key2);
+ if (bio_detain(tc->pool->prison, &key2, bio, &cell2)) {
+ cell_release_singleton(cell, bio);
+ break;
+ }
+
+ if (io_overlaps_block(pool, bio)) {
+ /*
+ * IO may still be going to the destination block. We must
+ * quiesce before we can do the removal.
+ */
+ m = get_next_mapping(pool);
+ m->tc = tc;
+ m->pass_discard = (!lookup_result.shared) & pool->pf.discard_passdown;
+ m->virt_block = block;
+ m->data_block = lookup_result.block;
+ m->cell = cell;
+ m->cell2 = cell2;
+ m->err = 0;
+ m->bio = bio;
+
+ if (!ds_add_work(&pool->all_io_ds, &m->list)) {
+ list_add(&m->list, &pool->prepared_discards);
+ wake_worker(pool);
+ }
+ } else {
+ /*
+ * This path is hit if people are ignoring
+ * limits->discard_granularity. It ignores any
+ * part of the discard that is in a subsequent
+ * block.
+ */
+ sector_t offset = bio->bi_sector - (block << pool->block_shift);
+ unsigned remaining = (pool->sectors_per_block - offset) << 9;
+ bio->bi_size = min(bio->bi_size, remaining);
+
+ cell_release_singleton(cell, bio);
+ cell_release_singleton(cell2, bio);
+ remap_and_issue(tc, bio, lookup_result.block);
+ }
+ break;
+
+ case -ENODATA:
+ /*
+ * It isn't provisioned, just forget it.
+ */
+ cell_release_singleton(cell, bio);
+ bio_endio(bio, 0);
+ break;
+
+ default:
+ DMERR("discard: find block unexpectedly returned %d", r);
+ cell_release_singleton(cell, bio);
+ bio_io_error(bio);
+ break;
+ }
+}
+
static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block,
struct cell_key *key,
struct dm_thin_lookup_result *lookup_result,
@@ -1113,8 +1265,8 @@ static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block,
r = alloc_data_block(tc, &data_block);
switch (r) {
case 0:
- schedule_copy(tc, block, lookup_result->block,
- data_block, cell, bio);
+ schedule_internal_copy(tc, block, lookup_result->block,
+ data_block, cell, bio);
break;
case -ENOSPC:
@@ -1147,13 +1299,9 @@ static void process_shared_bio(struct thin_c *tc, struct bio *bio,
if (bio_data_dir(bio) == WRITE)
break_sharing(tc, bio, block, &key, lookup_result, cell);
else {
- struct endio_hook *h;
- h = mempool_alloc(pool->endio_hook_pool, GFP_NOIO);
+ struct endio_hook *h = dm_get_mapinfo(bio)->ptr;
- h->tc = tc;
- h->entry = ds_inc(&pool->ds);
- save_and_set_endio(bio, &h->saved_bi_end_io, shared_read_endio);
- dm_get_mapinfo(bio)->ptr = h;
+ h->shared_read_entry = ds_inc(&pool->shared_read_ds);
cell_release_singleton(cell, bio);
remap_and_issue(tc, bio, lookup_result->block);
@@ -1188,7 +1336,10 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block
r = alloc_data_block(tc, &data_block);
switch (r) {
case 0:
- schedule_zero(tc, block, data_block, cell, bio);
+ if (tc->origin_dev)
+ schedule_external_copy(tc, block, data_block, cell, bio);
+ else
+ schedule_zero(tc, block, data_block, cell, bio);
break;
case -ENOSPC:
@@ -1239,16 +1390,27 @@ static void process_bio(struct thin_c *tc, struct bio *bio)
break;
case -ENODATA:
- provision_block(tc, bio, block, cell);
+ if (bio_data_dir(bio) == READ && tc->origin_dev) {
+ cell_release_singleton(cell, bio);
+ remap_to_origin_and_issue(tc, bio);
+ } else
+ provision_block(tc, bio, block, cell);
break;
default:
DMERR("dm_thin_find_block() failed, error = %d", r);
+ cell_release_singleton(cell, bio);
bio_io_error(bio);
break;
}
}
+static int need_commit_due_to_time(struct pool *pool)
+{
+ return jiffies < pool->last_commit_jiffies ||
+ jiffies > pool->last_commit_jiffies + COMMIT_PERIOD;
+}
+
static void process_deferred_bios(struct pool *pool)
{
unsigned long flags;
@@ -1264,7 +1426,9 @@ static void process_deferred_bios(struct pool *pool)
spin_unlock_irqrestore(&pool->lock, flags);
while ((bio = bio_list_pop(&bios))) {
- struct thin_c *tc = dm_get_mapinfo(bio)->ptr;
+ struct endio_hook *h = dm_get_mapinfo(bio)->ptr;
+ struct thin_c *tc = h->tc;
+
/*
* If we've got no free new_mapping structs, and processing
* this bio might require one, we pause until there are some
@@ -1277,7 +1441,11 @@ static void process_deferred_bios(struct pool *pool)
break;
}
- process_bio(tc, bio);
+
+ if (bio->bi_rw & REQ_DISCARD)
+ process_discard(tc, bio);
+ else
+ process_bio(tc, bio);
}
/*
@@ -1290,7 +1458,7 @@ static void process_deferred_bios(struct pool *pool)
bio_list_init(&pool->deferred_flush_bios);
spin_unlock_irqrestore(&pool->lock, flags);
- if (bio_list_empty(&bios))
+ if (bio_list_empty(&bios) && !need_commit_due_to_time(pool))
return;
r = dm_pool_commit_metadata(pool->pmd);
@@ -1301,6 +1469,7 @@ static void process_deferred_bios(struct pool *pool)
bio_io_error(bio);
return;
}
+ pool->last_commit_jiffies = jiffies;
while ((bio = bio_list_pop(&bios)))
generic_make_request(bio);
@@ -1310,10 +1479,22 @@ static void do_worker(struct work_struct *ws)
{
struct pool *pool = container_of(ws, struct pool, worker);
- process_prepared_mappings(pool);
+ process_prepared(pool, &pool->prepared_mappings, process_prepared_mapping);
+ process_prepared(pool, &pool->prepared_discards, process_prepared_discard);
process_deferred_bios(pool);
}
+/*
+ * We want to commit periodically so that not too much
+ * unwritten data builds up.
+ */
+static void do_waker(struct work_struct *ws)
+{
+ struct pool *pool = container_of(to_delayed_work(ws), struct pool, waker);
+ wake_worker(pool);
+ queue_delayed_work(pool->wq, &pool->waker, COMMIT_PERIOD);
+}
+
/*----------------------------------------------------------------*/
/*
@@ -1335,6 +1516,19 @@ static void thin_defer_bio(struct thin_c *tc, struct bio *bio)
wake_worker(pool);
}
+static struct endio_hook *thin_hook_bio(struct thin_c *tc, struct bio *bio)
+{
+ struct pool *pool = tc->pool;
+ struct endio_hook *h = mempool_alloc(pool->endio_hook_pool, GFP_NOIO);
+
+ h->tc = tc;
+ h->shared_read_entry = NULL;
+ h->all_io_entry = bio->bi_rw & REQ_DISCARD ? NULL : ds_inc(&pool->all_io_ds);
+ h->overwrite_mapping = NULL;
+
+ return h;
+}
+
/*
* Non-blocking function called from the thin target's map function.
*/
@@ -1347,12 +1541,8 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio,
struct dm_thin_device *td = tc->td;
struct dm_thin_lookup_result result;
- /*
- * Save the thin context for easy access from the deferred bio later.
- */
- map_context->ptr = tc;
-
- if (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) {
+ map_context->ptr = thin_hook_bio(tc, bio);
+ if (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA)) {
thin_defer_bio(tc, bio);
return DM_MAPIO_SUBMITTED;
}
@@ -1434,7 +1624,7 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti)
pool->ti = ti;
pool->low_water_blocks = pt->low_water_blocks;
- pool->zero_new_blocks = pt->zero_new_blocks;
+ pool->pf = pt->pf;
return 0;
}
@@ -1448,6 +1638,14 @@ static void unbind_control_target(struct pool *pool, struct dm_target *ti)
/*----------------------------------------------------------------
* Pool creation
*--------------------------------------------------------------*/
+/* Initialize pool features. */
+static void pool_features_init(struct pool_features *pf)
+{
+ pf->zero_new_blocks = 1;
+ pf->discard_enabled = 1;
+ pf->discard_passdown = 1;
+}
+
static void __pool_destroy(struct pool *pool)
{
__pool_table_remove(pool);
@@ -1495,7 +1693,7 @@ static struct pool *pool_create(struct mapped_device *pool_md,
pool->block_shift = ffs(block_size) - 1;
pool->offset_mask = block_size - 1;
pool->low_water_blocks = 0;
- pool->zero_new_blocks = 1;
+ pool_features_init(&pool->pf);
pool->prison = prison_create(PRISON_CELLS);
if (!pool->prison) {
*error = "Error creating pool's bio prison";
@@ -1523,14 +1721,17 @@ static struct pool *pool_create(struct mapped_device *pool_md,
}
INIT_WORK(&pool->worker, do_worker);
+ INIT_DELAYED_WORK(&pool->waker, do_waker);
spin_lock_init(&pool->lock);
bio_list_init(&pool->deferred_bios);
bio_list_init(&pool->deferred_flush_bios);
INIT_LIST_HEAD(&pool->prepared_mappings);
+ INIT_LIST_HEAD(&pool->prepared_discards);
pool->low_water_triggered = 0;
pool->no_free_space = 0;
bio_list_init(&pool->retry_on_resume_list);
- ds_init(&pool->ds);
+ ds_init(&pool->shared_read_ds);
+ ds_init(&pool->all_io_ds);
pool->next_mapping = NULL;
pool->mapping_pool =
@@ -1549,6 +1750,7 @@ static struct pool *pool_create(struct mapped_device *pool_md,
goto bad_endio_hook_pool;
}
pool->ref_count = 1;
+ pool->last_commit_jiffies = jiffies;
pool->pool_md = pool_md;
pool->md_dev = metadata_dev;
__pool_table_insert(pool);
@@ -1588,7 +1790,8 @@ static void __pool_dec(struct pool *pool)
static struct pool *__pool_find(struct mapped_device *pool_md,
struct block_device *metadata_dev,
- unsigned long block_size, char **error)
+ unsigned long block_size, char **error,
+ int *created)
{
struct pool *pool = __pool_table_lookup_metadata_dev(metadata_dev);
@@ -1604,8 +1807,10 @@ static struct pool *__pool_find(struct mapped_device *pool_md,
return ERR_PTR(-EINVAL);
__pool_inc(pool);
- } else
+ } else {
pool = pool_create(pool_md, metadata_dev, block_size, error);
+ *created = 1;
+ }
}
return pool;
@@ -1629,10 +1834,6 @@ static void pool_dtr(struct dm_target *ti)
mutex_unlock(&dm_thin_pool_table.mutex);
}
-struct pool_features {
- unsigned zero_new_blocks:1;
-};
-
static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
struct dm_target *ti)
{
@@ -1641,7 +1842,7 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
const char *arg_name;
static struct dm_arg _args[] = {
- {0, 1, "Invalid number of pool feature arguments"},
+ {0, 3, "Invalid number of pool feature arguments"},
};
/*
@@ -1661,6 +1862,12 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
if (!strcasecmp(arg_name, "skip_block_zeroing")) {
pf->zero_new_blocks = 0;
continue;
+ } else if (!strcasecmp(arg_name, "ignore_discard")) {
+ pf->discard_enabled = 0;
+ continue;
+ } else if (!strcasecmp(arg_name, "no_discard_passdown")) {
+ pf->discard_passdown = 0;
+ continue;
}
ti->error = "Unrecognised pool feature requested";
@@ -1678,10 +1885,12 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
*
* Optional feature arguments are:
* skip_block_zeroing: skips the zeroing of newly-provisioned blocks.
+ * ignore_discard: disable discard
+ * no_discard_passdown: don't pass discards down to the data device
*/
static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
- int r;
+ int r, pool_created = 0;
struct pool_c *pt;
struct pool *pool;
struct pool_features pf;
@@ -1691,6 +1900,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
dm_block_t low_water_blocks;
struct dm_dev *metadata_dev;
sector_t metadata_dev_size;
+ char b[BDEVNAME_SIZE];
/*
* FIXME Remove validation from scope of lock.
@@ -1712,11 +1922,9 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
}
metadata_dev_size = i_size_read(metadata_dev->bdev->bd_inode) >> SECTOR_SHIFT;
- if (metadata_dev_size > METADATA_DEV_MAX_SECTORS) {
- ti->error = "Metadata device is too large";
- r = -EINVAL;
- goto out_metadata;
- }
+ if (metadata_dev_size > THIN_METADATA_MAX_SECTORS_WARNING)
+ DMWARN("Metadata device %s is larger than %u sectors: excess space will not be used.",
+ bdevname(metadata_dev->bdev, b), THIN_METADATA_MAX_SECTORS);
r = dm_get_device(ti, argv[1], FMODE_READ | FMODE_WRITE, &data_dev);
if (r) {
@@ -1742,8 +1950,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
/*
* Set default pool features.
*/
- memset(&pf, 0, sizeof(pf));
- pf.zero_new_blocks = 1;
+ pool_features_init(&pf);
dm_consume_args(&as, 4);
r = parse_pool_features(&as, &pf, ti);
@@ -1757,20 +1964,58 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
}
pool = __pool_find(dm_table_get_md(ti->table), metadata_dev->bdev,
- block_size, &ti->error);
+ block_size, &ti->error, &pool_created);
if (IS_ERR(pool)) {
r = PTR_ERR(pool);
goto out_free_pt;
}
+ /*
+ * 'pool_created' reflects whether this is the first table load.
+ * Top level discard support is not allowed to be changed after
+ * initial load. This would require a pool reload to trigger thin
+ * device changes.
+ */
+ if (!pool_created && pf.discard_enabled != pool->pf.discard_enabled) {
+ ti->error = "Discard support cannot be disabled once enabled";
+ r = -EINVAL;
+ goto out_flags_changed;
+ }
+
+ /*
+ * If discard_passdown was enabled verify that the data device
+ * supports discards. Disable discard_passdown if not; otherwise
+ * -EOPNOTSUPP will be returned.
+ */
+ if (pf.discard_passdown) {
+ struct request_queue *q = bdev_get_queue(data_dev->bdev);
+ if (!q || !blk_queue_discard(q)) {
+ DMWARN("Discard unsupported by data device: Disabling discard passdown.");
+ pf.discard_passdown = 0;
+ }
+ }
+
pt->pool = pool;
pt->ti = ti;
pt->metadata_dev = metadata_dev;
pt->data_dev = data_dev;
pt->low_water_blocks = low_water_blocks;
- pt->zero_new_blocks = pf.zero_new_blocks;
+ pt->pf = pf;
ti->num_flush_requests = 1;
- ti->num_discard_requests = 0;
+ /*
+ * Only need to enable discards if the pool should pass
+ * them down to the data device. The thin device's discard
+ * processing will cause mappings to be removed from the btree.
+ */
+ if (pf.discard_enabled && pf.discard_passdown) {
+ ti->num_discard_requests = 1;
+ /*
+ * Setting 'discards_supported' circumvents the normal
+ * stacking of discard limits (this keeps the pool and
+ * thin devices' discard limits consistent).
+ */
+ ti->discards_supported = 1;
+ }
ti->private = pt;
pt->callbacks.congested_fn = pool_is_congested;
@@ -1780,6 +2025,8 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
return 0;
+out_flags_changed:
+ __pool_dec(pool);
out_free_pt:
kfree(pt);
out:
@@ -1878,7 +2125,7 @@ static void pool_resume(struct dm_target *ti)
__requeue_bios(pool);
spin_unlock_irqrestore(&pool->lock, flags);
- wake_worker(pool);
+ do_waker(&pool->waker.work);
}
static void pool_postsuspend(struct dm_target *ti)
@@ -1887,6 +2134,7 @@ static void pool_postsuspend(struct dm_target *ti)
struct pool_c *pt = ti->private;
struct pool *pool = pt->pool;
+ cancel_delayed_work(&pool->waker);
flush_workqueue(pool->wq);
r = dm_pool_commit_metadata(pool->pmd);
@@ -2067,7 +2315,7 @@ static int pool_message(struct dm_target *ti, unsigned argc, char **argv)
static int pool_status(struct dm_target *ti, status_type_t type,
char *result, unsigned maxlen)
{
- int r;
+ int r, count;
unsigned sz = 0;
uint64_t transaction_id;
dm_block_t nr_free_blocks_data;
@@ -2130,10 +2378,19 @@ static int pool_status(struct dm_target *ti, status_type_t type,
(unsigned long)pool->sectors_per_block,
(unsigned long long)pt->low_water_blocks);
- DMEMIT("%u ", !pool->zero_new_blocks);
+ count = !pool->pf.zero_new_blocks + !pool->pf.discard_enabled +
+ !pool->pf.discard_passdown;
+ DMEMIT("%u ", count);
- if (!pool->zero_new_blocks)
+ if (!pool->pf.zero_new_blocks)
DMEMIT("skip_block_zeroing ");
+
+ if (!pool->pf.discard_enabled)
+ DMEMIT("ignore_discard ");
+
+ if (!pool->pf.discard_passdown)
+ DMEMIT("no_discard_passdown ");
+
break;
}
@@ -2162,6 +2419,21 @@ static int pool_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
}
+static void set_discard_limits(struct pool *pool, struct queue_limits *limits)
+{
+ /*
+ * FIXME: these limits may be incompatible with the pool's data device
+ */
+ limits->max_discard_sectors = pool->sectors_per_block;
+
+ /*
+ * This is just a hint, and not enforced. We have to cope with
+ * bios that overlap 2 blocks.
+ */
+ limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
+ limits->discard_zeroes_data = pool->pf.zero_new_blocks;
+}
+
static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
{
struct pool_c *pt = ti->private;
@@ -2169,13 +2441,15 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
blk_limits_io_min(limits, 0);
blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
+ if (pool->pf.discard_enabled)
+ set_discard_limits(pool, limits);
}
static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
- .version = {1, 0, 0},
+ .version = {1, 1, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
@@ -2202,6 +2476,8 @@ static void thin_dtr(struct dm_target *ti)
__pool_dec(tc->pool);
dm_pool_close_thin_device(tc->td);
dm_put_device(ti, tc->pool_dev);
+ if (tc->origin_dev)
+ dm_put_device(ti, tc->origin_dev);
kfree(tc);
mutex_unlock(&dm_thin_pool_table.mutex);
@@ -2210,21 +2486,25 @@ static void thin_dtr(struct dm_target *ti)
/*
* Thin target parameters:
*
- * <pool_dev> <dev_id>
+ * <pool_dev> <dev_id> [origin_dev]
*
* pool_dev: the path to the pool (eg, /dev/mapper/my_pool)
* dev_id: the internal device identifier
+ * origin_dev: a device external to the pool that should act as the origin
+ *
+ * If the pool device has discards disabled, they get disabled for the thin
+ * device as well.
*/
static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
int r;
struct thin_c *tc;
- struct dm_dev *pool_dev;
+ struct dm_dev *pool_dev, *origin_dev;
struct mapped_device *pool_md;
mutex_lock(&dm_thin_pool_table.mutex);
- if (argc != 2) {
+ if (argc != 2 && argc != 3) {
ti->error = "Invalid argument count";
r = -EINVAL;
goto out_unlock;
@@ -2237,6 +2517,15 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto out_unlock;
}
+ if (argc == 3) {
+ r = dm_get_device(ti, argv[2], FMODE_READ, &origin_dev);
+ if (r) {
+ ti->error = "Error opening origin device";
+ goto bad_origin_dev;
+ }
+ tc->origin_dev = origin_dev;
+ }
+
r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &pool_dev);
if (r) {
ti->error = "Error opening pool device";
@@ -2273,8 +2562,12 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
ti->split_io = tc->pool->sectors_per_block;
ti->num_flush_requests = 1;
- ti->num_discard_requests = 0;
- ti->discards_supported = 0;
+
+ /* In case the pool supports discards, pass them on. */
+ if (tc->pool->pf.discard_enabled) {
+ ti->discards_supported = 1;
+ ti->num_discard_requests = 1;
+ }
dm_put(pool_md);
@@ -2289,6 +2582,9 @@ bad_pool_lookup:
bad_common:
dm_put_device(ti, tc->pool_dev);
bad_pool_dev:
+ if (tc->origin_dev)
+ dm_put_device(ti, tc->origin_dev);
+bad_origin_dev:
kfree(tc);
out_unlock:
mutex_unlock(&dm_thin_pool_table.mutex);
@@ -2299,11 +2595,46 @@ out_unlock:
static int thin_map(struct dm_target *ti, struct bio *bio,
union map_info *map_context)
{
- bio->bi_sector -= ti->begin;
+ bio->bi_sector = dm_target_offset(ti, bio->bi_sector);
return thin_bio_map(ti, bio, map_context);
}
+static int thin_endio(struct dm_target *ti,
+ struct bio *bio, int err,
+ union map_info *map_context)
+{
+ unsigned long flags;
+ struct endio_hook *h = map_context->ptr;
+ struct list_head work;
+ struct new_mapping *m, *tmp;
+ struct pool *pool = h->tc->pool;
+
+ if (h->shared_read_entry) {
+ INIT_LIST_HEAD(&work);
+ ds_dec(h->shared_read_entry, &work);
+
+ spin_lock_irqsave(&pool->lock, flags);
+ list_for_each_entry_safe(m, tmp, &work, list) {
+ list_del(&m->list);
+ m->quiesced = 1;
+ __maybe_add_mapping(m);
+ }
+ spin_unlock_irqrestore(&pool->lock, flags);
+ }
+
+ if (h->all_io_entry) {
+ INIT_LIST_HEAD(&work);
+ ds_dec(h->all_io_entry, &work);
+ list_for_each_entry_safe(m, tmp, &work, list)
+ list_add(&m->list, &pool->prepared_discards);
+ }
+
+ mempool_free(h, pool->endio_hook_pool);
+
+ return 0;
+}
+
static void thin_postsuspend(struct dm_target *ti)
{
if (dm_noflush_suspending(ti))
@@ -2347,6 +2678,8 @@ static int thin_status(struct dm_target *ti, status_type_t type,
DMEMIT("%s %lu",
format_dev_t(buf, tc->pool_dev->bdev->bd_dev),
(unsigned long) tc->dev_id);
+ if (tc->origin_dev)
+ DMEMIT(" %s", format_dev_t(buf, tc->origin_dev->bdev->bd_dev));
break;
}
}
@@ -2377,18 +2710,21 @@ static int thin_iterate_devices(struct dm_target *ti,
static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
{
struct thin_c *tc = ti->private;
+ struct pool *pool = tc->pool;
blk_limits_io_min(limits, 0);
- blk_limits_io_opt(limits, tc->pool->sectors_per_block << SECTOR_SHIFT);
+ blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
+ set_discard_limits(pool, limits);
}
static struct target_type thin_target = {
.name = "thin",
- .version = {1, 0, 0},
+ .version = {1, 1, 0},
.module = THIS_MODULE,
.ctr = thin_ctr,
.dtr = thin_dtr,
.map = thin_map,
+ .end_io = thin_endio,
.postsuspend = thin_postsuspend,
.status = thin_status,
.iterate_devices = thin_iterate_devices,
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c
new file mode 100644
index 0000000..fa365d3
--- /dev/null
+++ b/drivers/md/dm-verity.c
@@ -0,0 +1,913 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Author: Mikulas Patocka <mpatocka@redhat.com>
+ *
+ * Based on Chromium dm-verity driver (C) 2011 The Chromium OS Authors
+ *
+ * This file is released under the GPLv2.
+ *
+ * In the file "/sys/module/dm_verity/parameters/prefetch_cluster" you can set
+ * default prefetch value. Data are read in "prefetch_cluster" chunks from the
+ * hash device. Setting this greatly improves performance when data and hash
+ * are on the same disk on different partitions on devices with poor random
+ * access behavior.
+ */
+
+#include "dm-bufio.h"
+
+#include <linux/module.h>
+#include <linux/device-mapper.h>
+#include <crypto/hash.h>
+
+#define DM_MSG_PREFIX "verity"
+
+#define DM_VERITY_IO_VEC_INLINE 16
+#define DM_VERITY_MEMPOOL_SIZE 4
+#define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144
+
+#define DM_VERITY_MAX_LEVELS 63
+
+static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
+
+module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR);
+
+struct dm_verity {
+ struct dm_dev *data_dev;
+ struct dm_dev *hash_dev;
+ struct dm_target *ti;
+ struct dm_bufio_client *bufio;
+ char *alg_name;
+ struct crypto_shash *tfm;
+ u8 *root_digest; /* digest of the root block */
+ u8 *salt; /* salt: its size is salt_size */
+ unsigned salt_size;
+ sector_t data_start; /* data offset in 512-byte sectors */
+ sector_t hash_start; /* hash start in blocks */
+ sector_t data_blocks; /* the number of data blocks */
+ sector_t hash_blocks; /* the number of hash blocks */
+ unsigned char data_dev_block_bits; /* log2(data blocksize) */
+ unsigned char hash_dev_block_bits; /* log2(hash blocksize) */
+ unsigned char hash_per_block_bits; /* log2(hashes in hash block) */
+ unsigned char levels; /* the number of tree levels */
+ unsigned char version;
+ unsigned digest_size; /* digest size for the current hash algorithm */
+ unsigned shash_descsize;/* the size of temporary space for crypto */
+ int hash_failed; /* set to 1 if hash of any block failed */
+
+ mempool_t *io_mempool; /* mempool of struct dm_verity_io */
+ mempool_t *vec_mempool; /* mempool of bio vector */
+
+ struct workqueue_struct *verify_wq;
+
+ /* starting blocks for each tree level. 0 is the lowest level. */
+ sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
+};
+
+struct dm_verity_io {
+ struct dm_verity *v;
+ struct bio *bio;
+
+ /* original values of bio->bi_end_io and bio->bi_private */
+ bio_end_io_t *orig_bi_end_io;
+ void *orig_bi_private;
+
+ sector_t block;
+ unsigned n_blocks;
+
+ /* saved bio vector */
+ struct bio_vec *io_vec;
+ unsigned io_vec_size;
+
+ struct work_struct work;
+
+ /* A space for short vectors; longer vectors are allocated separately. */
+ struct bio_vec io_vec_inline[DM_VERITY_IO_VEC_INLINE];
+
+ /*
+ * Three variably-size fields follow this struct:
+ *
+ * u8 hash_desc[v->shash_descsize];
+ * u8 real_digest[v->digest_size];
+ * u8 want_digest[v->digest_size];
+ *
+ * To access them use: io_hash_desc(), io_real_digest() and io_want_digest().
+ */
+};
+
+static struct shash_desc *io_hash_desc(struct dm_verity *v, struct dm_verity_io *io)
+{
+ return (struct shash_desc *)(io + 1);
+}
+
+static u8 *io_real_digest(struct dm_verity *v, struct dm_verity_io *io)
+{
+ return (u8 *)(io + 1) + v->shash_descsize;
+}
+
+static u8 *io_want_digest(struct dm_verity *v, struct dm_verity_io *io)
+{
+ return (u8 *)(io + 1) + v->shash_descsize + v->digest_size;
+}
+
+/*
+ * Auxiliary structure appended to each dm-bufio buffer. If the value
+ * hash_verified is nonzero, hash of the block has been verified.
+ *
+ * The variable hash_verified is set to 0 when allocating the buffer, then
+ * it can be changed to 1 and it is never reset to 0 again.
+ *
+ * There is no lock around this value, a race condition can at worst cause
+ * that multiple processes verify the hash of the same buffer simultaneously
+ * and write 1 to hash_verified simultaneously.
+ * This condition is harmless, so we don't need locking.
+ */
+struct buffer_aux {
+ int hash_verified;
+};
+
+/*
+ * Initialize struct buffer_aux for a freshly created buffer.
+ */
+static void dm_bufio_alloc_callback(struct dm_buffer *buf)
+{
+ struct buffer_aux *aux = dm_bufio_get_aux_data(buf);
+
+ aux->hash_verified = 0;
+}
+
+/*
+ * Translate input sector number to the sector number on the target device.
+ */
+static sector_t verity_map_sector(struct dm_verity *v, sector_t bi_sector)
+{
+ return v->data_start + dm_target_offset(v->ti, bi_sector);
+}
+
+/*
+ * Return hash position of a specified block at a specified tree level
+ * (0 is the lowest level).
+ * The lowest "hash_per_block_bits"-bits of the result denote hash position
+ * inside a hash block. The remaining bits denote location of the hash block.
+ */
+static sector_t verity_position_at_level(struct dm_verity *v, sector_t block,
+ int level)
+{
+ return block >> (level * v->hash_per_block_bits);
+}
+
+static void verity_hash_at_level(struct dm_verity *v, sector_t block, int level,
+ sector_t *hash_block, unsigned *offset)
+{
+ sector_t position = verity_position_at_level(v, block, level);
+ unsigned idx;
+
+ *hash_block = v->hash_level_block[level] + (position >> v->hash_per_block_bits);
+
+ if (!offset)
+ return;
+
+ idx = position & ((1 << v->hash_per_block_bits) - 1);
+ if (!v->version)
+ *offset = idx * v->digest_size;
+ else
+ *offset = idx << (v->hash_dev_block_bits - v->hash_per_block_bits);
+}
+
+/*
+ * Verify hash of a metadata block pertaining to the specified data block
+ * ("block" argument) at a specified level ("level" argument).
+ *
+ * On successful return, io_want_digest(v, io) contains the hash value for
+ * a lower tree level or for the data block (if we're at the lowest leve).
+ *
+ * If "skip_unverified" is true, unverified buffer is skipped and 1 is returned.
+ * If "skip_unverified" is false, unverified buffer is hashed and verified
+ * against current value of io_want_digest(v, io).
+ */
+static int verity_verify_level(struct dm_verity_io *io, sector_t block,
+ int level, bool skip_unverified)
+{
+ struct dm_verity *v = io->v;
+ struct dm_buffer *buf;
+ struct buffer_aux *aux;
+ u8 *data;
+ int r;
+ sector_t hash_block;
+ unsigned offset;
+
+ verity_hash_at_level(v, block, level, &hash_block, &offset);
+
+ data = dm_bufio_read(v->bufio, hash_block, &buf);
+ if (unlikely(IS_ERR(data)))
+ return PTR_ERR(data);
+
+ aux = dm_bufio_get_aux_data(buf);
+
+ if (!aux->hash_verified) {
+ struct shash_desc *desc;
+ u8 *result;
+
+ if (skip_unverified) {
+ r = 1;
+ goto release_ret_r;
+ }
+
+ desc = io_hash_desc(v, io);
+ desc->tfm = v->tfm;
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ r = crypto_shash_init(desc);
+ if (r < 0) {
+ DMERR("crypto_shash_init failed: %d", r);
+ goto release_ret_r;
+ }
+
+ if (likely(v->version >= 1)) {
+ r = crypto_shash_update(desc, v->salt, v->salt_size);
+ if (r < 0) {
+ DMERR("crypto_shash_update failed: %d", r);
+ goto release_ret_r;
+ }
+ }
+
+ r = crypto_shash_update(desc, data, 1 << v->hash_dev_block_bits);
+ if (r < 0) {
+ DMERR("crypto_shash_update failed: %d", r);
+ goto release_ret_r;
+ }
+
+ if (!v->version) {
+ r = crypto_shash_update(desc, v->salt, v->salt_size);
+ if (r < 0) {
+ DMERR("crypto_shash_update failed: %d", r);
+ goto release_ret_r;
+ }
+ }
+
+ result = io_real_digest(v, io);
+ r = crypto_shash_final(desc, result);
+ if (r < 0) {
+ DMERR("crypto_shash_final failed: %d", r);
+ goto release_ret_r;
+ }
+ if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
+ DMERR_LIMIT("metadata block %llu is corrupted",
+ (unsigned long long)hash_block);
+ v->hash_failed = 1;
+ r = -EIO;
+ goto release_ret_r;
+ } else
+ aux->hash_verified = 1;
+ }
+
+ data += offset;
+
+ memcpy(io_want_digest(v, io), data, v->digest_size);
+
+ dm_bufio_release(buf);
+ return 0;
+
+release_ret_r:
+ dm_bufio_release(buf);
+
+ return r;
+}
+
+/*
+ * Verify one "dm_verity_io" structure.
+ */
+static int verity_verify_io(struct dm_verity_io *io)
+{
+ struct dm_verity *v = io->v;
+ unsigned b;
+ int i;
+ unsigned vector = 0, offset = 0;
+
+ for (b = 0; b < io->n_blocks; b++) {
+ struct shash_desc *desc;
+ u8 *result;
+ int r;
+ unsigned todo;
+
+ if (likely(v->levels)) {
+ /*
+ * First, we try to get the requested hash for
+ * the current block. If the hash block itself is
+ * verified, zero is returned. If it isn't, this
+ * function returns 0 and we fall back to whole
+ * chain verification.
+ */
+ int r = verity_verify_level(io, io->block + b, 0, true);
+ if (likely(!r))
+ goto test_block_hash;
+ if (r < 0)
+ return r;
+ }
+
+ memcpy(io_want_digest(v, io), v->root_digest, v->digest_size);
+
+ for (i = v->levels - 1; i >= 0; i--) {
+ int r = verity_verify_level(io, io->block + b, i, false);
+ if (unlikely(r))
+ return r;
+ }
+
+test_block_hash:
+ desc = io_hash_desc(v, io);
+ desc->tfm = v->tfm;
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ r = crypto_shash_init(desc);
+ if (r < 0) {
+ DMERR("crypto_shash_init failed: %d", r);
+ return r;
+ }
+
+ if (likely(v->version >= 1)) {
+ r = crypto_shash_update(desc, v->salt, v->salt_size);
+ if (r < 0) {
+ DMERR("crypto_shash_update failed: %d", r);
+ return r;
+ }
+ }
+
+ todo = 1 << v->data_dev_block_bits;
+ do {
+ struct bio_vec *bv;
+ u8 *page;
+ unsigned len;
+
+ BUG_ON(vector >= io->io_vec_size);
+ bv = &io->io_vec[vector];
+ page = kmap_atomic(bv->bv_page);
+ len = bv->bv_len - offset;
+ if (likely(len >= todo))
+ len = todo;
+ r = crypto_shash_update(desc,
+ page + bv->bv_offset + offset, len);
+ kunmap_atomic(page);
+ if (r < 0) {
+ DMERR("crypto_shash_update failed: %d", r);
+ return r;
+ }
+ offset += len;
+ if (likely(offset == bv->bv_len)) {
+ offset = 0;
+ vector++;
+ }
+ todo -= len;
+ } while (todo);
+
+ if (!v->version) {
+ r = crypto_shash_update(desc, v->salt, v->salt_size);
+ if (r < 0) {
+ DMERR("crypto_shash_update failed: %d", r);
+ return r;
+ }
+ }
+
+ result = io_real_digest(v, io);
+ r = crypto_shash_final(desc, result);
+ if (r < 0) {
+ DMERR("crypto_shash_final failed: %d", r);
+ return r;
+ }
+ if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
+ DMERR_LIMIT("data block %llu is corrupted",
+ (unsigned long long)(io->block + b));
+ v->hash_failed = 1;
+ return -EIO;
+ }
+ }
+ BUG_ON(vector != io->io_vec_size);
+ BUG_ON(offset);
+
+ return 0;
+}
+
+/*
+ * End one "io" structure with a given error.
+ */
+static void verity_finish_io(struct dm_verity_io *io, int error)
+{
+ struct bio *bio = io->bio;
+ struct dm_verity *v = io->v;
+
+ bio->bi_end_io = io->orig_bi_end_io;
+ bio->bi_private = io->orig_bi_private;
+
+ if (io->io_vec != io->io_vec_inline)
+ mempool_free(io->io_vec, v->vec_mempool);
+
+ mempool_free(io, v->io_mempool);
+
+ bio_endio(bio, error);
+}
+
+static void verity_work(struct work_struct *w)
+{
+ struct dm_verity_io *io = container_of(w, struct dm_verity_io, work);
+
+ verity_finish_io(io, verity_verify_io(io));
+}
+
+static void verity_end_io(struct bio *bio, int error)
+{
+ struct dm_verity_io *io = bio->bi_private;
+
+ if (error) {
+ verity_finish_io(io, error);
+ return;
+ }
+
+ INIT_WORK(&io->work, verity_work);
+ queue_work(io->v->verify_wq, &io->work);
+}
+
+/*
+ * Prefetch buffers for the specified io.
+ * The root buffer is not prefetched, it is assumed that it will be cached
+ * all the time.
+ */
+static void verity_prefetch_io(struct dm_verity *v, struct dm_verity_io *io)
+{
+ int i;
+
+ for (i = v->levels - 2; i >= 0; i--) {
+ sector_t hash_block_start;
+ sector_t hash_block_end;
+ verity_hash_at_level(v, io->block, i, &hash_block_start, NULL);
+ verity_hash_at_level(v, io->block + io->n_blocks - 1, i, &hash_block_end, NULL);
+ if (!i) {
+ unsigned cluster = *(volatile unsigned *)&dm_verity_prefetch_cluster;
+
+ cluster >>= v->data_dev_block_bits;
+ if (unlikely(!cluster))
+ goto no_prefetch_cluster;
+
+ if (unlikely(cluster & (cluster - 1)))
+ cluster = 1 << (fls(cluster) - 1);
+
+ hash_block_start &= ~(sector_t)(cluster - 1);
+ hash_block_end |= cluster - 1;
+ if (unlikely(hash_block_end >= v->hash_blocks))
+ hash_block_end = v->hash_blocks - 1;
+ }
+no_prefetch_cluster:
+ dm_bufio_prefetch(v->bufio, hash_block_start,
+ hash_block_end - hash_block_start + 1);
+ }
+}
+
+/*
+ * Bio map function. It allocates dm_verity_io structure and bio vector and
+ * fills them. Then it issues prefetches and the I/O.
+ */
+static int verity_map(struct dm_target *ti, struct bio *bio,
+ union map_info *map_context)
+{
+ struct dm_verity *v = ti->private;
+ struct dm_verity_io *io;
+
+ bio->bi_bdev = v->data_dev->bdev;
+ bio->bi_sector = verity_map_sector(v, bio->bi_sector);
+
+ if (((unsigned)bio->bi_sector | bio_sectors(bio)) &
+ ((1 << (v->data_dev_block_bits - SECTOR_SHIFT)) - 1)) {
+ DMERR_LIMIT("unaligned io");
+ return -EIO;
+ }
+
+ if ((bio->bi_sector + bio_sectors(bio)) >>
+ (v->data_dev_block_bits - SECTOR_SHIFT) > v->data_blocks) {
+ DMERR_LIMIT("io out of range");
+ return -EIO;
+ }
+
+ if (bio_data_dir(bio) == WRITE)
+ return -EIO;
+
+ io = mempool_alloc(v->io_mempool, GFP_NOIO);
+ io->v = v;
+ io->bio = bio;
+ io->orig_bi_end_io = bio->bi_end_io;
+ io->orig_bi_private = bio->bi_private;
+ io->block = bio->bi_sector >> (v->data_dev_block_bits - SECTOR_SHIFT);
+ io->n_blocks = bio->bi_size >> v->data_dev_block_bits;
+
+ bio->bi_end_io = verity_end_io;
+ bio->bi_private = io;
+ io->io_vec_size = bio->bi_vcnt - bio->bi_idx;
+ if (io->io_vec_size < DM_VERITY_IO_VEC_INLINE)
+ io->io_vec = io->io_vec_inline;
+ else
+ io->io_vec = mempool_alloc(v->vec_mempool, GFP_NOIO);
+ memcpy(io->io_vec, bio_iovec(bio),
+ io->io_vec_size * sizeof(struct bio_vec));
+
+ verity_prefetch_io(v, io);
+
+ generic_make_request(bio);
+
+ return DM_MAPIO_SUBMITTED;
+}
+
+/*
+ * Status: V (valid) or C (corruption found)
+ */
+static int verity_status(struct dm_target *ti, status_type_t type,
+ char *result, unsigned maxlen)
+{
+ struct dm_verity *v = ti->private;
+ unsigned sz = 0;
+ unsigned x;
+
+ switch (type) {
+ case STATUSTYPE_INFO:
+ DMEMIT("%c", v->hash_failed ? 'C' : 'V');
+ break;
+ case STATUSTYPE_TABLE:
+ DMEMIT("%u %s %s %u %u %llu %llu %s ",
+ v->version,
+ v->data_dev->name,
+ v->hash_dev->name,
+ 1 << v->data_dev_block_bits,
+ 1 << v->hash_dev_block_bits,
+ (unsigned long long)v->data_blocks,
+ (unsigned long long)v->hash_start,
+ v->alg_name
+ );
+ for (x = 0; x < v->digest_size; x++)
+ DMEMIT("%02x", v->root_digest[x]);
+ DMEMIT(" ");
+ if (!v->salt_size)
+ DMEMIT("-");
+ else
+ for (x = 0; x < v->salt_size; x++)
+ DMEMIT("%02x", v->salt[x]);
+ break;
+ }
+
+ return 0;
+}
+
+static int verity_ioctl(struct dm_target *ti, unsigned cmd,
+ unsigned long arg)
+{
+ struct dm_verity *v = ti->private;
+ int r = 0;
+
+ if (v->data_start ||
+ ti->len != i_size_read(v->data_dev->bdev->bd_inode) >> SECTOR_SHIFT)
+ r = scsi_verify_blk_ioctl(NULL, cmd);
+
+ return r ? : __blkdev_driver_ioctl(v->data_dev->bdev, v->data_dev->mode,
+ cmd, arg);
+}
+
+static int verity_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
+ struct bio_vec *biovec, int max_size)
+{
+ struct dm_verity *v = ti->private;
+ struct request_queue *q = bdev_get_queue(v->data_dev->bdev);
+
+ if (!q->merge_bvec_fn)
+ return max_size;
+
+ bvm->bi_bdev = v->data_dev->bdev;
+ bvm->bi_sector = verity_map_sector(v, bvm->bi_sector);
+
+ return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
+}
+
+static int verity_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data)
+{
+ struct dm_verity *v = ti->private;
+
+ return fn(ti, v->data_dev, v->data_start, ti->len, data);
+}
+
+static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
+{
+ struct dm_verity *v = ti->private;
+
+ if (limits->logical_block_size < 1 << v->data_dev_block_bits)
+ limits->logical_block_size = 1 << v->data_dev_block_bits;
+
+ if (limits->physical_block_size < 1 << v->data_dev_block_bits)
+ limits->physical_block_size = 1 << v->data_dev_block_bits;
+
+ blk_limits_io_min(limits, limits->logical_block_size);
+}
+
+static void verity_dtr(struct dm_target *ti)
+{
+ struct dm_verity *v = ti->private;
+
+ if (v->verify_wq)
+ destroy_workqueue(v->verify_wq);
+
+ if (v->vec_mempool)
+ mempool_destroy(v->vec_mempool);
+
+ if (v->io_mempool)
+ mempool_destroy(v->io_mempool);
+
+ if (v->bufio)
+ dm_bufio_client_destroy(v->bufio);
+
+ kfree(v->salt);
+ kfree(v->root_digest);
+
+ if (v->tfm)
+ crypto_free_shash(v->tfm);
+
+ kfree(v->alg_name);
+
+ if (v->hash_dev)
+ dm_put_device(ti, v->hash_dev);
+
+ if (v->data_dev)
+ dm_put_device(ti, v->data_dev);
+
+ kfree(v);
+}
+
+/*
+ * Target parameters:
+ * <version> The current format is version 1.
+ * Vsn 0 is compatible with original Chromium OS releases.
+ * <data device>
+ * <hash device>
+ * <data block size>
+ * <hash block size>
+ * <the number of data blocks>
+ * <hash start block>
+ * <algorithm>
+ * <digest>
+ * <salt> Hex string or "-" if no salt.
+ */
+static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
+{
+ struct dm_verity *v;
+ unsigned num;
+ unsigned long long num_ll;
+ int r;
+ int i;
+ sector_t hash_position;
+ char dummy;
+
+ v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
+ if (!v) {
+ ti->error = "Cannot allocate verity structure";
+ return -ENOMEM;
+ }
+ ti->private = v;
+ v->ti = ti;
+
+ if ((dm_table_get_mode(ti->table) & ~FMODE_READ)) {
+ ti->error = "Device must be readonly";
+ r = -EINVAL;
+ goto bad;
+ }
+
+ if (argc != 10) {
+ ti->error = "Invalid argument count: exactly 10 arguments required";
+ r = -EINVAL;
+ goto bad;
+ }
+
+ if (sscanf(argv[0], "%d%c", &num, &dummy) != 1 ||
+ num < 0 || num > 1) {
+ ti->error = "Invalid version";
+ r = -EINVAL;
+ goto bad;
+ }
+ v->version = num;
+
+ r = dm_get_device(ti, argv[1], FMODE_READ, &v->data_dev);
+ if (r) {
+ ti->error = "Data device lookup failed";
+ goto bad;
+ }
+
+ r = dm_get_device(ti, argv[2], FMODE_READ, &v->hash_dev);
+ if (r) {
+ ti->error = "Data device lookup failed";
+ goto bad;
+ }
+
+ if (sscanf(argv[3], "%u%c", &num, &dummy) != 1 ||
+ !num || (num & (num - 1)) ||
+ num < bdev_logical_block_size(v->data_dev->bdev) ||
+ num > PAGE_SIZE) {
+ ti->error = "Invalid data device block size";
+ r = -EINVAL;
+ goto bad;
+ }
+ v->data_dev_block_bits = ffs(num) - 1;
+
+ if (sscanf(argv[4], "%u%c", &num, &dummy) != 1 ||
+ !num || (num & (num - 1)) ||
+ num < bdev_logical_block_size(v->hash_dev->bdev) ||
+ num > INT_MAX) {
+ ti->error = "Invalid hash device block size";
+ r = -EINVAL;
+ goto bad;
+ }
+ v->hash_dev_block_bits = ffs(num) - 1;
+
+ if (sscanf(argv[5], "%llu%c", &num_ll, &dummy) != 1 ||
+ num_ll << (v->data_dev_block_bits - SECTOR_SHIFT) !=
+ (sector_t)num_ll << (v->data_dev_block_bits - SECTOR_SHIFT)) {
+ ti->error = "Invalid data blocks";
+ r = -EINVAL;
+ goto bad;
+ }
+ v->data_blocks = num_ll;
+
+ if (ti->len > (v->data_blocks << (v->data_dev_block_bits - SECTOR_SHIFT))) {
+ ti->error = "Data device is too small";
+ r = -EINVAL;
+ goto bad;
+ }
+
+ if (sscanf(argv[6], "%llu%c", &num_ll, &dummy) != 1 ||
+ num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT) !=
+ (sector_t)num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT)) {
+ ti->error = "Invalid hash start";
+ r = -EINVAL;
+ goto bad;
+ }
+ v->hash_start = num_ll;
+
+ v->alg_name = kstrdup(argv[7], GFP_KERNEL);
+ if (!v->alg_name) {
+ ti->error = "Cannot allocate algorithm name";
+ r = -ENOMEM;
+ goto bad;
+ }
+
+ v->tfm = crypto_alloc_shash(v->alg_name, 0, 0);
+ if (IS_ERR(v->tfm)) {
+ ti->error = "Cannot initialize hash function";
+ r = PTR_ERR(v->tfm);
+ v->tfm = NULL;
+ goto bad;
+ }
+ v->digest_size = crypto_shash_digestsize(v->tfm);
+ if ((1 << v->hash_dev_block_bits) < v->digest_size * 2) {
+ ti->error = "Digest size too big";
+ r = -EINVAL;
+ goto bad;
+ }
+ v->shash_descsize =
+ sizeof(struct shash_desc) + crypto_shash_descsize(v->tfm);
+
+ v->root_digest = kmalloc(v->digest_size, GFP_KERNEL);
+ if (!v->root_digest) {
+ ti->error = "Cannot allocate root digest";
+ r = -ENOMEM;
+ goto bad;
+ }
+ if (strlen(argv[8]) != v->digest_size * 2 ||
+ hex2bin(v->root_digest, argv[8], v->digest_size)) {
+ ti->error = "Invalid root digest";
+ r = -EINVAL;
+ goto bad;
+ }
+
+ if (strcmp(argv[9], "-")) {
+ v->salt_size = strlen(argv[9]) / 2;
+ v->salt = kmalloc(v->salt_size, GFP_KERNEL);
+ if (!v->salt) {
+ ti->error = "Cannot allocate salt";
+ r = -ENOMEM;
+ goto bad;
+ }
+ if (strlen(argv[9]) != v->salt_size * 2 ||
+ hex2bin(v->salt, argv[9], v->salt_size)) {
+ ti->error = "Invalid salt";
+ r = -EINVAL;
+ goto bad;
+ }
+ }
+
+ v->hash_per_block_bits =
+ fls((1 << v->hash_dev_block_bits) / v->digest_size) - 1;
+
+ v->levels = 0;
+ if (v->data_blocks)
+ while (v->hash_per_block_bits * v->levels < 64 &&
+ (unsigned long long)(v->data_blocks - 1) >>
+ (v->hash_per_block_bits * v->levels))
+ v->levels++;
+
+ if (v->levels > DM_VERITY_MAX_LEVELS) {
+ ti->error = "Too many tree levels";
+ r = -E2BIG;
+ goto bad;
+ }
+
+ hash_position = v->hash_start;
+ for (i = v->levels - 1; i >= 0; i--) {
+ sector_t s;
+ v->hash_level_block[i] = hash_position;
+ s = verity_position_at_level(v, v->data_blocks, i);
+ s = (s >> v->hash_per_block_bits) +
+ !!(s & ((1 << v->hash_per_block_bits) - 1));
+ if (hash_position + s < hash_position) {
+ ti->error = "Hash device offset overflow";
+ r = -E2BIG;
+ goto bad;
+ }
+ hash_position += s;
+ }
+ v->hash_blocks = hash_position;
+
+ v->bufio = dm_bufio_client_create(v->hash_dev->bdev,
+ 1 << v->hash_dev_block_bits, 1, sizeof(struct buffer_aux),
+ dm_bufio_alloc_callback, NULL);
+ if (IS_ERR(v->bufio)) {
+ ti->error = "Cannot initialize dm-bufio";
+ r = PTR_ERR(v->bufio);
+ v->bufio = NULL;
+ goto bad;
+ }
+
+ if (dm_bufio_get_device_size(v->bufio) < v->hash_blocks) {
+ ti->error = "Hash device is too small";
+ r = -E2BIG;
+ goto bad;
+ }
+
+ v->io_mempool = mempool_create_kmalloc_pool(DM_VERITY_MEMPOOL_SIZE,
+ sizeof(struct dm_verity_io) + v->shash_descsize + v->digest_size * 2);
+ if (!v->io_mempool) {
+ ti->error = "Cannot allocate io mempool";
+ r = -ENOMEM;
+ goto bad;
+ }
+
+ v->vec_mempool = mempool_create_kmalloc_pool(DM_VERITY_MEMPOOL_SIZE,
+ BIO_MAX_PAGES * sizeof(struct bio_vec));
+ if (!v->vec_mempool) {
+ ti->error = "Cannot allocate vector mempool";
+ r = -ENOMEM;
+ goto bad;
+ }
+
+ /* WQ_UNBOUND greatly improves performance when running on ramdisk */
+ v->verify_wq = alloc_workqueue("kverityd", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | WQ_UNBOUND, num_online_cpus());
+ if (!v->verify_wq) {
+ ti->error = "Cannot allocate workqueue";
+ r = -ENOMEM;
+ goto bad;
+ }
+
+ return 0;
+
+bad:
+ verity_dtr(ti);
+
+ return r;
+}
+
+static struct target_type verity_target = {
+ .name = "verity",
+ .version = {1, 0, 0},
+ .module = THIS_MODULE,
+ .ctr = verity_ctr,
+ .dtr = verity_dtr,
+ .map = verity_map,
+ .status = verity_status,
+ .ioctl = verity_ioctl,
+ .merge = verity_merge,
+ .iterate_devices = verity_iterate_devices,
+ .io_hints = verity_io_hints,
+};
+
+static int __init dm_verity_init(void)
+{
+ int r;
+
+ r = dm_register_target(&verity_target);
+ if (r < 0)
+ DMERR("register failed %d", r);
+
+ return r;
+}
+
+static void __exit dm_verity_exit(void)
+{
+ dm_unregister_target(&verity_target);
+}
+
+module_init(dm_verity_init);
+module_exit(dm_verity_exit);
+
+MODULE_AUTHOR("Mikulas Patocka <mpatocka@redhat.com>");
+MODULE_AUTHOR("Mandeep Baines <msb@chromium.org>");
+MODULE_AUTHOR("Will Drewry <wad@chromium.org>");
+MODULE_DESCRIPTION(DM_NAME " target for transparent disk integrity checking");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index b89c548..e24143c 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1016,6 +1016,7 @@ static void __map_bio(struct dm_target *ti, struct bio *clone,
/*
* Store bio_set for cleanup.
*/
+ clone->bi_end_io = NULL;
clone->bi_private = md->bs;
bio_put(clone);
free_tio(md, tio);
diff --git a/drivers/md/persistent-data/dm-btree-internal.h b/drivers/md/persistent-data/dm-btree-internal.h
index d279c76..5709bfe 100644
--- a/drivers/md/persistent-data/dm-btree-internal.h
+++ b/drivers/md/persistent-data/dm-btree-internal.h
@@ -108,12 +108,9 @@ static inline void *value_base(struct node *n)
return &n->keys[le32_to_cpu(n->header.max_entries)];
}
-/*
- * FIXME: Now that value size is stored in node we don't need the third parm.
- */
-static inline void *value_ptr(struct node *n, uint32_t index, size_t value_size)
+static inline void *value_ptr(struct node *n, uint32_t index)
{
- BUG_ON(value_size != le32_to_cpu(n->header.value_size));
+ uint32_t value_size = le32_to_cpu(n->header.value_size);
return value_base(n) + (value_size * index);
}
diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c
index 023fbc2..aa71e23 100644
--- a/drivers/md/persistent-data/dm-btree-remove.c
+++ b/drivers/md/persistent-data/dm-btree-remove.c
@@ -61,20 +61,20 @@ static void node_shift(struct node *n, int shift)
if (shift < 0) {
shift = -shift;
BUG_ON(shift > nr_entries);
- BUG_ON((void *) key_ptr(n, shift) >= value_ptr(n, shift, value_size));
+ BUG_ON((void *) key_ptr(n, shift) >= value_ptr(n, shift));
memmove(key_ptr(n, 0),
key_ptr(n, shift),
(nr_entries - shift) * sizeof(__le64));
- memmove(value_ptr(n, 0, value_size),
- value_ptr(n, shift, value_size),
+ memmove(value_ptr(n, 0),
+ value_ptr(n, shift),
(nr_entries - shift) * value_size);
} else {
BUG_ON(nr_entries + shift > le32_to_cpu(n->header.max_entries));
memmove(key_ptr(n, shift),
key_ptr(n, 0),
nr_entries * sizeof(__le64));
- memmove(value_ptr(n, shift, value_size),
- value_ptr(n, 0, value_size),
+ memmove(value_ptr(n, shift),
+ value_ptr(n, 0),
nr_entries * value_size);
}
}
@@ -91,16 +91,16 @@ static void node_copy(struct node *left, struct node *right, int shift)
memcpy(key_ptr(left, nr_left),
key_ptr(right, 0),
shift * sizeof(__le64));
- memcpy(value_ptr(left, nr_left, value_size),
- value_ptr(right, 0, value_size),
+ memcpy(value_ptr(left, nr_left),
+ value_ptr(right, 0),
shift * value_size);
} else {
BUG_ON(shift > le32_to_cpu(right->header.max_entries));
memcpy(key_ptr(right, 0),
key_ptr(left, nr_left - shift),
shift * sizeof(__le64));
- memcpy(value_ptr(right, 0, value_size),
- value_ptr(left, nr_left - shift, value_size),
+ memcpy(value_ptr(right, 0),
+ value_ptr(left, nr_left - shift),
shift * value_size);
}
}
@@ -120,26 +120,17 @@ static void delete_at(struct node *n, unsigned index)
key_ptr(n, index + 1),
nr_to_copy * sizeof(__le64));
- memmove(value_ptr(n, index, value_size),
- value_ptr(n, index + 1, value_size),
+ memmove(value_ptr(n, index),
+ value_ptr(n, index + 1),
nr_to_copy * value_size);
}
n->header.nr_entries = cpu_to_le32(nr_entries - 1);
}
-static unsigned del_threshold(struct node *n)
-{
- return le32_to_cpu(n->header.max_entries) / 3;
-}
-
static unsigned merge_threshold(struct node *n)
{
- /*
- * The extra one is because we know we're potentially going to
- * delete an entry.
- */
- return 2 * (le32_to_cpu(n->header.max_entries) / 3) + 1;
+ return le32_to_cpu(n->header.max_entries) / 3;
}
struct child {
@@ -175,7 +166,7 @@ static int init_child(struct dm_btree_info *info, struct node *parent,
if (inc)
inc_children(info->tm, result->n, &le64_type);
- *((__le64 *) value_ptr(parent, index, sizeof(__le64))) =
+ *((__le64 *) value_ptr(parent, index)) =
cpu_to_le64(dm_block_location(result->block));
return 0;
@@ -188,6 +179,15 @@ static int exit_child(struct dm_btree_info *info, struct child *c)
static void shift(struct node *left, struct node *right, int count)
{
+ uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
+ uint32_t nr_right = le32_to_cpu(right->header.nr_entries);
+ uint32_t max_entries = le32_to_cpu(left->header.max_entries);
+ uint32_t r_max_entries = le32_to_cpu(right->header.max_entries);
+
+ BUG_ON(max_entries != r_max_entries);
+ BUG_ON(nr_left - count > max_entries);
+ BUG_ON(nr_right + count > max_entries);
+
if (!count)
return;
@@ -199,13 +199,8 @@ static void shift(struct node *left, struct node *right, int count)
node_shift(right, count);
}
- left->header.nr_entries =
- cpu_to_le32(le32_to_cpu(left->header.nr_entries) - count);
- BUG_ON(le32_to_cpu(left->header.nr_entries) > le32_to_cpu(left->header.max_entries));
-
- right->header.nr_entries =
- cpu_to_le32(le32_to_cpu(right->header.nr_entries) + count);
- BUG_ON(le32_to_cpu(right->header.nr_entries) > le32_to_cpu(right->header.max_entries));
+ left->header.nr_entries = cpu_to_le32(nr_left - count);
+ right->header.nr_entries = cpu_to_le32(nr_right + count);
}
static void __rebalance2(struct dm_btree_info *info, struct node *parent,
@@ -215,8 +210,9 @@ static void __rebalance2(struct dm_btree_info *info, struct node *parent,
struct node *right = r->n;
uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
uint32_t nr_right = le32_to_cpu(right->header.nr_entries);
+ unsigned threshold = 2 * merge_threshold(left) + 1;
- if (nr_left + nr_right <= merge_threshold(left)) {
+ if (nr_left + nr_right < threshold) {
/*
* Merge
*/
@@ -234,9 +230,6 @@ static void __rebalance2(struct dm_btree_info *info, struct node *parent,
* Rebalance.
*/
unsigned target_left = (nr_left + nr_right) / 2;
- unsigned shift_ = nr_left - target_left;
- BUG_ON(le32_to_cpu(left->header.max_entries) <= nr_left - shift_);
- BUG_ON(le32_to_cpu(right->header.max_entries) <= nr_right + shift_);
shift(left, right, nr_left - target_left);
*key_ptr(parent, r->index) = right->keys[0];
}
@@ -272,6 +265,84 @@ static int rebalance2(struct shadow_spine *s, struct dm_btree_info *info,
return exit_child(info, &right);
}
+/*
+ * We dump as many entries from center as possible into left, then the rest
+ * in right, then rebalance2. This wastes some cpu, but I want something
+ * simple atm.
+ */
+static void delete_center_node(struct dm_btree_info *info, struct node *parent,
+ struct child *l, struct child *c, struct child *r,
+ struct node *left, struct node *center, struct node *right,
+ uint32_t nr_left, uint32_t nr_center, uint32_t nr_right)
+{
+ uint32_t max_entries = le32_to_cpu(left->header.max_entries);
+ unsigned shift = min(max_entries - nr_left, nr_center);
+
+ BUG_ON(nr_left + shift > max_entries);
+ node_copy(left, center, -shift);
+ left->header.nr_entries = cpu_to_le32(nr_left + shift);
+
+ if (shift != nr_center) {
+ shift = nr_center - shift;
+ BUG_ON((nr_right + shift) > max_entries);
+ node_shift(right, shift);
+ node_copy(center, right, shift);
+ right->header.nr_entries = cpu_to_le32(nr_right + shift);
+ }
+ *key_ptr(parent, r->index) = right->keys[0];
+
+ delete_at(parent, c->index);
+ r->index--;
+
+ dm_tm_dec(info->tm, dm_block_location(c->block));
+ __rebalance2(info, parent, l, r);
+}
+
+/*
+ * Redistributes entries among 3 sibling nodes.
+ */
+static void redistribute3(struct dm_btree_info *info, struct node *parent,
+ struct child *l, struct child *c, struct child *r,
+ struct node *left, struct node *center, struct node *right,
+ uint32_t nr_left, uint32_t nr_center, uint32_t nr_right)
+{
+ int s;
+ uint32_t max_entries = le32_to_cpu(left->header.max_entries);
+ unsigned target = (nr_left + nr_center + nr_right) / 3;
+ BUG_ON(target > max_entries);
+
+ if (nr_left < nr_right) {
+ s = nr_left - target;
+
+ if (s < 0 && nr_center < -s) {
+ /* not enough in central node */
+ shift(left, center, nr_center);
+ s = nr_center - target;
+ shift(left, right, s);
+ nr_right += s;
+ } else
+ shift(left, center, s);
+
+ shift(center, right, target - nr_right);
+
+ } else {
+ s = target - nr_right;
+ if (s > 0 && nr_center < s) {
+ /* not enough in central node */
+ shift(center, right, nr_center);
+ s = target - nr_center;
+ shift(left, right, s);
+ nr_left -= s;
+ } else
+ shift(center, right, s);
+
+ shift(left, center, nr_left - target);
+ }
+
+ *key_ptr(parent, c->index) = center->keys[0];
+ *key_ptr(parent, r->index) = right->keys[0];
+}
+
static void __rebalance3(struct dm_btree_info *info, struct node *parent,
struct child *l, struct child *c, struct child *r)
{
@@ -282,62 +353,18 @@ static void __rebalance3(struct dm_btree_info *info, struct node *parent,
uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
uint32_t nr_center = le32_to_cpu(center->header.nr_entries);
uint32_t nr_right = le32_to_cpu(right->header.nr_entries);
- uint32_t max_entries = le32_to_cpu(left->header.max_entries);
- unsigned target;
+ unsigned threshold = merge_threshold(left) * 4 + 1;
BUG_ON(left->header.max_entries != center->header.max_entries);
BUG_ON(center->header.max_entries != right->header.max_entries);
- if (((nr_left + nr_center + nr_right) / 2) < merge_threshold(center)) {
- /*
- * Delete center node:
- *
- * We dump as many entries from center as possible into
- * left, then the rest in right, then rebalance2. This
- * wastes some cpu, but I want something simple atm.
- */
- unsigned shift = min(max_entries - nr_left, nr_center);
-
- BUG_ON(nr_left + shift > max_entries);
- node_copy(left, center, -shift);
- left->header.nr_entries = cpu_to_le32(nr_left + shift);
-
- if (shift != nr_center) {
- shift = nr_center - shift;
- BUG_ON((nr_right + shift) >= max_entries);
- node_shift(right, shift);
- node_copy(center, right, shift);
- right->header.nr_entries = cpu_to_le32(nr_right + shift);
- }
- *key_ptr(parent, r->index) = right->keys[0];
-
- delete_at(parent, c->index);
- r->index--;
-
- dm_tm_dec(info->tm, dm_block_location(c->block));
- __rebalance2(info, parent, l, r);
-
- return;
- }
-
- /*
- * Rebalance
- */
- target = (nr_left + nr_center + nr_right) / 3;
- BUG_ON(target > max_entries);
-
- /*
- * Adjust the left node
- */
- shift(left, center, nr_left - target);
-
- /*
- * Adjust the right node
- */
- shift(center, right, target - nr_right);
- *key_ptr(parent, c->index) = center->keys[0];
- *key_ptr(parent, r->index) = right->keys[0];
+ if ((nr_left + nr_center + nr_right) < threshold)
+ delete_center_node(info, parent, l, c, r, left, center, right,
+ nr_left, nr_center, nr_right);
+ else
+ redistribute3(info, parent, l, c, r, left, center, right,
+ nr_left, nr_center, nr_right);
}
static int rebalance3(struct shadow_spine *s, struct dm_btree_info *info,
@@ -441,9 +468,6 @@ static int rebalance_children(struct shadow_spine *s,
if (r)
return r;
- if (child_entries > del_threshold(n))
- return 0;
-
has_left_sibling = i > 0;
has_right_sibling = i < (le32_to_cpu(n->header.nr_entries) - 1);
@@ -496,7 +520,7 @@ static int remove_raw(struct shadow_spine *s, struct dm_btree_info *info,
*/
if (shadow_has_parent(s)) {
__le64 location = cpu_to_le64(dm_block_location(shadow_current(s)));
- memcpy(value_ptr(dm_block_data(shadow_parent(s)), i, sizeof(__le64)),
+ memcpy(value_ptr(dm_block_data(shadow_parent(s)), i),
&location, sizeof(__le64));
}
@@ -553,7 +577,7 @@ int dm_btree_remove(struct dm_btree_info *info, dm_block_t root,
if (info->value_type.dec)
info->value_type.dec(info->value_type.context,
- value_ptr(n, index, info->value_type.size));
+ value_ptr(n, index));
delete_at(n, index);
}
diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c
index bd1e7ff..d12b2cc 100644
--- a/drivers/md/persistent-data/dm-btree.c
+++ b/drivers/md/persistent-data/dm-btree.c
@@ -74,8 +74,7 @@ void inc_children(struct dm_transaction_manager *tm, struct node *n,
dm_tm_inc(tm, value64(n, i));
else if (vt->inc)
for (i = 0; i < nr_entries; i++)
- vt->inc(vt->context,
- value_ptr(n, i, vt->size));
+ vt->inc(vt->context, value_ptr(n, i));
}
static int insert_at(size_t value_size, struct node *node, unsigned index,
@@ -281,7 +280,7 @@ int dm_btree_del(struct dm_btree_info *info, dm_block_t root)
for (i = 0; i < f->nr_children; i++)
info->value_type.dec(info->value_type.context,
- value_ptr(f->n, i, info->value_type.size));
+ value_ptr(f->n, i));
}
f->current_child = f->nr_children;
}
@@ -320,7 +319,7 @@ static int btree_lookup_raw(struct ro_spine *s, dm_block_t block, uint64_t key,
} while (!(flags & LEAF_NODE));
*result_key = le64_to_cpu(ro_node(s)->keys[i]);
- memcpy(v, value_ptr(ro_node(s), i, value_size), value_size);
+ memcpy(v, value_ptr(ro_node(s), i), value_size);
return 0;
}
@@ -432,7 +431,7 @@ static int btree_split_sibling(struct shadow_spine *s, dm_block_t root,
size = le32_to_cpu(ln->header.flags) & INTERNAL_NODE ?
sizeof(uint64_t) : s->info->value_type.size;
- memcpy(value_ptr(rn, 0, size), value_ptr(ln, nr_left, size),
+ memcpy(value_ptr(rn, 0), value_ptr(ln, nr_left),
size * nr_right);
/*
@@ -443,7 +442,7 @@ static int btree_split_sibling(struct shadow_spine *s, dm_block_t root,
pn = dm_block_data(parent);
location = cpu_to_le64(dm_block_location(left));
__dm_bless_for_disk(&location);
- memcpy_disk(value_ptr(pn, parent_index, sizeof(__le64)),
+ memcpy_disk(value_ptr(pn, parent_index),
&location, sizeof(__le64));
location = cpu_to_le64(dm_block_location(right));
@@ -529,8 +528,8 @@ static int btree_split_beneath(struct shadow_spine *s, uint64_t key)
size = le32_to_cpu(pn->header.flags) & INTERNAL_NODE ?
sizeof(__le64) : s->info->value_type.size;
- memcpy(value_ptr(ln, 0, size), value_ptr(pn, 0, size), nr_left * size);
- memcpy(value_ptr(rn, 0, size), value_ptr(pn, nr_left, size),
+ memcpy(value_ptr(ln, 0), value_ptr(pn, 0), nr_left * size);
+ memcpy(value_ptr(rn, 0), value_ptr(pn, nr_left),
nr_right * size);
/* new_parent should just point to l and r now */
@@ -545,12 +544,12 @@ static int btree_split_beneath(struct shadow_spine *s, uint64_t key)
val = cpu_to_le64(dm_block_location(left));
__dm_bless_for_disk(&val);
pn->keys[0] = ln->keys[0];
- memcpy_disk(value_ptr(pn, 0, sizeof(__le64)), &val, sizeof(__le64));
+ memcpy_disk(value_ptr(pn, 0), &val, sizeof(__le64));
val = cpu_to_le64(dm_block_location(right));
__dm_bless_for_disk(&val);
pn->keys[1] = rn->keys[0];
- memcpy_disk(value_ptr(pn, 1, sizeof(__le64)), &val, sizeof(__le64));
+ memcpy_disk(value_ptr(pn, 1), &val, sizeof(__le64));
/*
* rejig the spine. This is ugly, since it knows too
@@ -595,7 +594,7 @@ static int btree_insert_raw(struct shadow_spine *s, dm_block_t root,
__le64 location = cpu_to_le64(dm_block_location(shadow_current(s)));
__dm_bless_for_disk(&location);
- memcpy_disk(value_ptr(dm_block_data(shadow_parent(s)), i, sizeof(uint64_t)),
+ memcpy_disk(value_ptr(dm_block_data(shadow_parent(s)), i),
&location, sizeof(__le64));
}
@@ -710,12 +709,12 @@ static int insert(struct dm_btree_info *info, dm_block_t root,
(!info->value_type.equal ||
!info->value_type.equal(
info->value_type.context,
- value_ptr(n, index, info->value_type.size),
+ value_ptr(n, index),
value))) {
info->value_type.dec(info->value_type.context,
- value_ptr(n, index, info->value_type.size));
+ value_ptr(n, index));
}
- memcpy_disk(value_ptr(n, index, info->value_type.size),
+ memcpy_disk(value_ptr(n, index),
value, info->value_type.size);
}
diff --git a/drivers/md/persistent-data/dm-space-map-common.c b/drivers/md/persistent-data/dm-space-map-common.c
index df2494c..ff3beed 100644
--- a/drivers/md/persistent-data/dm-space-map-common.c
+++ b/drivers/md/persistent-data/dm-space-map-common.c
@@ -405,8 +405,6 @@ int sm_ll_insert(struct ll_disk *ll, dm_block_t b,
if (r < 0)
return r;
-#if 0
- /* FIXME: dm_btree_remove doesn't handle this yet */
if (old > 2) {
r = dm_btree_remove(&ll->ref_count_info,
ll->ref_count_root,
@@ -414,7 +412,6 @@ int sm_ll_insert(struct ll_disk *ll, dm_block_t b,
if (r)
return r;
}
-#endif
} else {
__le32 le_rc = cpu_to_le32(ref_count);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index e4b5c03..73970cd 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -29,7 +29,6 @@
#include <linux/ioctl.h>
#include <linux/wait.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#include "dmxdev.h"
static int debug;
diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c
index 864b627..e24ec53 100644
--- a/drivers/media/dvb/firewire/firedtv-fw.c
+++ b/drivers/media/dvb/firewire/firedtv-fw.c
@@ -20,7 +20,6 @@
#include <linux/workqueue.h>
#include <asm/page.h>
-#include <asm/system.h>
#include <dvb_demux.h>
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index 6ecbcf6..4bd8bd5 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -53,7 +53,6 @@
#include <asm/unaligned.h>
#include <asm/byteorder.h>
-#include <asm/system.h>
#include <linux/dvb/frontend.h>
diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
index 421cf73..f6b52d5 100644
--- a/drivers/media/media-devnode.c
+++ b/drivers/media/media-devnode.c
@@ -40,7 +40,6 @@
#include <linux/string.h>
#include <linux/types.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#include <media/media-devnode.h>
diff --git a/drivers/media/video/davinci/vpbe_osd.c b/drivers/media/video/davinci/vpbe_osd.c
index d6488b7..bba299d 100644
--- a/drivers/media/video/davinci/vpbe_osd.c
+++ b/drivers/media/video/davinci/vpbe_osd.c
@@ -28,7 +28,6 @@
#include <linux/clk.h>
#include <linux/slab.h>
-#include <mach/io.h>
#include <mach/cputype.h>
#include <mach/hardware.h>
diff --git a/drivers/media/video/davinci/vpbe_venc.c b/drivers/media/video/davinci/vpbe_venc.c
index 00e80f5..b21ecc8 100644
--- a/drivers/media/video/davinci/vpbe_venc.c
+++ b/drivers/media/video/davinci/vpbe_venc.c
@@ -27,7 +27,6 @@
#include <mach/hardware.h>
#include <mach/mux.h>
-#include <mach/io.h>
#include <mach/i2c.h>
#include <linux/io.h>
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index f767df9..2e22002 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -54,7 +54,6 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
#include <linux/dvb/video.h>
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index 7452277..93c35ef 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -286,7 +286,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0);
sg_dma_len(sg) = new_size;
- txd = ichan->dma_chan.device->device_prep_slave_sg(
+ txd = dmaengine_prep_slave_sg(
&ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
if (!txd)
diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c
index 4ed1c7c2..02194c0 100644
--- a/drivers/media/video/timblogiw.c
+++ b/drivers/media/video/timblogiw.c
@@ -564,7 +564,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
spin_unlock_irq(&fh->queue_lock);
- desc = fh->chan->device->device_prep_slave_sg(fh->chan,
+ desc = dmaengine_prep_slave_sg(fh->chan,
buf->sg, sg_elems, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
if (!desc) {
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 5c6100f..1baec83 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -55,7 +55,6 @@
#include <linux/spi/spi.h>
#endif
#include <asm/uaccess.h>
-#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/div64.h>
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 041804b..70bec54 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -26,7 +26,6 @@
#include <linux/kmod.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c
index c8ed7b6..1d31d72 100644
--- a/drivers/message/i2o/i2o_scsi.c
+++ b/drivers/message/i2o/i2o_scsi.c
@@ -57,7 +57,6 @@
#include <linux/scatterlist.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <linux/atomic.h>
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index 17dfe9b..87bd5ba 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -503,6 +503,101 @@ static void device_irq_exit(struct pm860x_chip *chip)
free_irq(chip->core_irq, chip);
}
+int pm8606_osc_enable(struct pm860x_chip *chip, unsigned short client)
+{
+ int ret = -EIO;
+ struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
+ chip->client : chip->companion;
+
+ dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
+ dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
+ __func__, chip->osc_vote,
+ chip->osc_status);
+
+ mutex_lock(&chip->osc_lock);
+ /* Update voting status */
+ chip->osc_vote |= client;
+ /* If reference group is off - turn on*/
+ if (chip->osc_status != PM8606_REF_GP_OSC_ON) {
+ chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
+ /* Enable Reference group Vsys */
+ if (pm860x_set_bits(i2c, PM8606_VSYS,
+ PM8606_VSYS_EN, PM8606_VSYS_EN))
+ goto out;
+
+ /*Enable Internal Oscillator */
+ if (pm860x_set_bits(i2c, PM8606_MISC,
+ PM8606_MISC_OSC_EN, PM8606_MISC_OSC_EN))
+ goto out;
+ /* Update status (only if writes succeed) */
+ chip->osc_status = PM8606_REF_GP_OSC_ON;
+ }
+ mutex_unlock(&chip->osc_lock);
+
+ dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
+ __func__, chip->osc_vote,
+ chip->osc_status, ret);
+ return 0;
+out:
+ mutex_unlock(&chip->osc_lock);
+ return ret;
+}
+EXPORT_SYMBOL(pm8606_osc_enable);
+
+int pm8606_osc_disable(struct pm860x_chip *chip, unsigned short client)
+{
+ int ret = -EIO;
+ struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
+ chip->client : chip->companion;
+
+ dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
+ dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
+ __func__, chip->osc_vote,
+ chip->osc_status);
+
+ mutex_lock(&chip->osc_lock);
+ /*Update voting status */
+ chip->osc_vote &= ~(client);
+ /* If reference group is off and this is the last client to release
+ * - turn off */
+ if ((chip->osc_status != PM8606_REF_GP_OSC_OFF) &&
+ (chip->osc_vote == REF_GP_NO_CLIENTS)) {
+ chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
+ /* Disable Reference group Vsys */
+ if (pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0))
+ goto out;
+ /* Disable Internal Oscillator */
+ if (pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0))
+ goto out;
+ chip->osc_status = PM8606_REF_GP_OSC_OFF;
+ }
+ mutex_unlock(&chip->osc_lock);
+
+ dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
+ __func__, chip->osc_vote,
+ chip->osc_status, ret);
+ return 0;
+out:
+ mutex_unlock(&chip->osc_lock);
+ return ret;
+}
+EXPORT_SYMBOL(pm8606_osc_disable);
+
+static void __devinit device_osc_init(struct i2c_client *i2c)
+{
+ struct pm860x_chip *chip = i2c_get_clientdata(i2c);
+
+ mutex_init(&chip->osc_lock);
+ /* init portofino reference group voting and status */
+ /* Disable Reference group Vsys */
+ pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0);
+ /* Disable Internal Oscillator */
+ pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0);
+
+ chip->osc_vote = REF_GP_NO_CLIENTS;
+ chip->osc_status = PM8606_REF_GP_OSC_OFF;
+}
+
static void __devinit device_bk_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
@@ -767,6 +862,15 @@ out:
return;
}
+static void __devinit device_8606_init(struct pm860x_chip *chip,
+ struct i2c_client *i2c,
+ struct pm860x_platform_data *pdata)
+{
+ device_osc_init(i2c);
+ device_bk_init(chip, pdata);
+ device_led_init(chip, pdata);
+}
+
int __devinit pm860x_device_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
@@ -774,8 +878,7 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip,
switch (chip->id) {
case CHIP_PM8606:
- device_bk_init(chip, pdata);
- device_led_init(chip, pdata);
+ device_8606_init(chip, chip->client, pdata);
break;
case CHIP_PM8607:
device_8607_init(chip, chip->client, pdata);
@@ -785,8 +888,7 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip,
if (chip->companion) {
switch (chip->id) {
case CHIP_PM8607:
- device_bk_init(chip, pdata);
- device_led_init(chip, pdata);
+ device_8606_init(chip, chip->companion, pdata);
break;
case CHIP_PM8606:
device_8607_init(chip, chip->companion, pdata);
diff --git a/drivers/mfd/88pm860x-i2c.c b/drivers/mfd/88pm860x-i2c.c
index f93dd95..b2cfdc4 100644
--- a/drivers/mfd/88pm860x-i2c.c
+++ b/drivers/mfd/88pm860x-i2c.c
@@ -334,10 +334,35 @@ static int __devexit pm860x_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int pm860x_suspend(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct pm860x_chip *chip = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(dev) && chip->wakeup_flag)
+ enable_irq_wake(chip->core_irq);
+ return 0;
+}
+
+static int pm860x_resume(struct device *dev)
+{
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct pm860x_chip *chip = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(dev) && chip->wakeup_flag)
+ disable_irq_wake(chip->core_irq);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm860x_pm_ops, pm860x_suspend, pm860x_resume);
+
static struct i2c_driver pm860x_driver = {
.driver = {
.name = "88PM860x",
.owner = THIS_MODULE,
+ .pm = &pm860x_pm_ops,
},
.probe = pm860x_probe,
.remove = __devexit_p(pm860x_remove),
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 1489c35..29f463c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -143,6 +143,21 @@ config TPS6507X
This driver can also be built as a module. If so, the module
will be called tps6507x.
+config MFD_TPS65217
+ tristate "TPS65217 Power Management / White LED chips"
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the TPS65217 series of
+ Power Management / White LED chips.
+ These include voltage regulators, lithium ion/polymer battery
+ charger, wled and other features that are often used in portable
+ devices.
+
+ This driver can also be built as a module. If so, the module
+ will be called tps65217.
+
config MFD_TPS6586X
bool "TPS6586x Power Management chips"
depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
@@ -162,6 +177,7 @@ config MFD_TPS65910
depends on I2C=y && GPIOLIB
select MFD_CORE
select GPIO_TPS65910
+ select REGMAP_I2C
help
if you say yes here you get support for the TPS65910 series of
Power Management chips.
@@ -171,7 +187,7 @@ config MFD_TPS65912
depends on GPIOLIB
config MFD_TPS65912_I2C
- bool "TPS95612 Power Management chip with I2C"
+ bool "TPS65912 Power Management chip with I2C"
select MFD_CORE
select MFD_TPS65912
depends on I2C=y && GPIOLIB
@@ -400,7 +416,7 @@ config MFD_MAX8997
depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
help
- Say yes here to support for Maxim Semiconductor MAX8998/8966.
+ Say yes here to support for Maxim Semiconductor MAX8997/8966.
This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic,
MUIC controls on chip.
This driver provides common support for accessing the device;
@@ -812,6 +828,18 @@ config MFD_PM8XXX_IRQ
config TPS65911_COMPARATOR
tristate
+config MFD_TPS65090
+ bool "TPS65090 Power Management chips"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the TPS65090 series of
+ Power Management chips.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
config MFD_AAT2870_CORE
bool "Support for the AnalogicTech AAT2870"
select MFD_CORE
@@ -831,6 +859,28 @@ config MFD_INTEL_MSIC
Passage) chip. This chip embeds audio, battery, GPIO, etc.
devices used in Intel Medfield platforms.
+config MFD_RC5T583
+ bool "Ricoh RC5T583 Power Management system device"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ Select this option to get support for the RICOH583 Power
+ Management system device.
+ This driver provides common support for accessing the device
+ through i2c interface. The device supports multiple sub-devices
+ like GPIO, interrupts, RTC, LDO and DCDC regulators, onkey.
+ Additional drivers must be enabled in order to use the
+ different functionality of the device.
+
+config MFD_ANATOP
+ bool "Support for Freescale i.MX on-chip ANATOP controller"
+ depends on SOC_IMX6Q
+ help
+ Select this option to enable Freescale i.MX on-chip ANATOP
+ MFD controller. This controller embeds regulator and
+ thermal devices for Freescale i.MX platforms.
+
endmenu
endif
@@ -848,8 +898,9 @@ config MCP_SA11X0
# Chip drivers
config MCP_UCB1200
- tristate "Support for UCB1200 / UCB1300"
- depends on MCP
+ bool "Support for UCB1200 / UCB1300"
+ depends on MCP_SA11X0
+ select MCP
config MCP_UCB1200_TS
tristate "Touchscreen interface support"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b953bab..05fa538 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o wm8994-regmap.o
obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
+obj-$(CONFIG_MFD_TPS65217) += tps65217.o
obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o
tps65912-objs := tps65912-core.o tps65912-irq.o
obj-$(CONFIG_MFD_TPS65912) += tps65912.o
@@ -109,6 +110,9 @@ obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
+obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
+obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o
+obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index d295941..1f08704 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -32,6 +32,7 @@
#define AB8500_IT_SOURCE6_REG 0x05
#define AB8500_IT_SOURCE7_REG 0x06
#define AB8500_IT_SOURCE8_REG 0x07
+#define AB9540_IT_SOURCE13_REG 0x0C
#define AB8500_IT_SOURCE19_REG 0x12
#define AB8500_IT_SOURCE20_REG 0x13
#define AB8500_IT_SOURCE21_REG 0x14
@@ -53,6 +54,7 @@
#define AB8500_IT_LATCH9_REG 0x28
#define AB8500_IT_LATCH10_REG 0x29
#define AB8500_IT_LATCH12_REG 0x2B
+#define AB9540_IT_LATCH13_REG 0x2C
#define AB8500_IT_LATCH19_REG 0x32
#define AB8500_IT_LATCH20_REG 0x33
#define AB8500_IT_LATCH21_REG 0x34
@@ -90,21 +92,39 @@
#define AB8500_IT_MASK24_REG 0x57
#define AB8500_REV_REG 0x80
+#define AB8500_IC_NAME_REG 0x82
#define AB8500_SWITCH_OFF_STATUS 0x00
#define AB8500_TURN_ON_STATUS 0x00
+#define AB9540_MODEM_CTRL2_REG 0x23
+#define AB9540_MODEM_CTRL2_SWDBBRSTN_BIT BIT(2)
+
/*
* Map interrupt numbers to the LATCH and MASK register offsets, Interrupt
- * numbers are indexed into this array with (num / 8).
+ * numbers are indexed into this array with (num / 8). The interupts are
+ * defined in linux/mfd/ab8500.h
*
* This is one off from the register names, i.e. AB8500_IT_MASK1_REG is at
* offset 0.
*/
+/* AB8500 support */
static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {
0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21,
};
+/* AB9540 support */
+static const int ab9540_irq_regoffset[AB9540_NUM_IRQ_REGS] = {
+ 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24,
+};
+
+static const char ab8500_version_str[][7] = {
+ [AB8500_VERSION_AB8500] = "AB8500",
+ [AB8500_VERSION_AB8505] = "AB8505",
+ [AB8500_VERSION_AB9540] = "AB9540",
+ [AB8500_VERSION_AB8540] = "AB8540",
+};
+
static int ab8500_get_chip_id(struct device *dev)
{
struct ab8500 *ab8500;
@@ -127,9 +147,7 @@ static int set_register_interruptible(struct ab8500 *ab8500, u8 bank,
dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data);
- ret = mutex_lock_interruptible(&ab8500->lock);
- if (ret)
- return ret;
+ mutex_lock(&ab8500->lock);
ret = ab8500->write(ab8500, addr, data);
if (ret < 0)
@@ -156,9 +174,7 @@ static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,
* bank on higher 8 bits and reg in lower */
u16 addr = ((u16)bank) << 8 | reg;
- ret = mutex_lock_interruptible(&ab8500->lock);
- if (ret)
- return ret;
+ mutex_lock(&ab8500->lock);
ret = ab8500->read(ab8500, addr);
if (ret < 0)
@@ -185,31 +201,38 @@ static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank,
u8 reg, u8 bitmask, u8 bitvalues)
{
int ret;
- u8 data;
/* put the u8 bank and u8 reg together into a an u16.
* bank on higher 8 bits and reg in lower */
u16 addr = ((u16)bank) << 8 | reg;
- ret = mutex_lock_interruptible(&ab8500->lock);
- if (ret)
- return ret;
+ mutex_lock(&ab8500->lock);
- ret = ab8500->read(ab8500, addr);
- if (ret < 0) {
- dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
- addr, ret);
- goto out;
- }
+ if (ab8500->write_masked == NULL) {
+ u8 data;
- data = (u8)ret;
- data = (~bitmask & data) | (bitmask & bitvalues);
+ ret = ab8500->read(ab8500, addr);
+ if (ret < 0) {
+ dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
+ addr, ret);
+ goto out;
+ }
- ret = ab8500->write(ab8500, addr, data);
- if (ret < 0)
- dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
- addr, ret);
+ data = (u8)ret;
+ data = (~bitmask & data) | (bitmask & bitvalues);
+
+ ret = ab8500->write(ab8500, addr, data);
+ if (ret < 0)
+ dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
+ addr, ret);
- dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr, data);
+ dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr,
+ data);
+ goto out;
+ }
+ ret = ab8500->write_masked(ab8500, addr, bitmask, bitvalues);
+ if (ret < 0)
+ dev_err(ab8500->dev, "failed to modify reg %#x: %d\n", addr,
+ ret);
out:
mutex_unlock(&ab8500->lock);
return ret;
@@ -248,7 +271,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
int i;
- for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
+ for (i = 0; i < ab8500->mask_size; i++) {
u8 old = ab8500->oldmask[i];
u8 new = ab8500->mask[i];
int reg;
@@ -256,14 +279,17 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
if (new == old)
continue;
- /* Interrupt register 12 doesn't exist prior to version 2.0 */
- if (ab8500_irq_regoffset[i] == 11 &&
- ab8500->chip_id < AB8500_CUT2P0)
+ /*
+ * Interrupt register 12 doesn't exist prior to AB8500 version
+ * 2.0
+ */
+ if (ab8500->irq_reg_offset[i] == 11 &&
+ is_ab8500_1p1_or_earlier(ab8500))
continue;
ab8500->oldmask[i] = new;
- reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i];
+ reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i];
set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new);
}
@@ -306,13 +332,16 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
dev_vdbg(ab8500->dev, "interrupt\n");
- for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
- int regoffset = ab8500_irq_regoffset[i];
+ for (i = 0; i < ab8500->mask_size; i++) {
+ int regoffset = ab8500->irq_reg_offset[i];
int status;
u8 value;
- /* Interrupt register 12 doesn't exist prior to version 2.0 */
- if (regoffset == 11 && ab8500->chip_id < AB8500_CUT2P0)
+ /*
+ * Interrupt register 12 doesn't exist prior to AB8500 version
+ * 2.0
+ */
+ if (regoffset == 11 && is_ab8500_1p1_or_earlier(ab8500))
continue;
status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
@@ -336,8 +365,16 @@ static int ab8500_irq_init(struct ab8500 *ab8500)
{
int base = ab8500->irq_base;
int irq;
+ int num_irqs;
+
+ if (is_ab9540(ab8500))
+ num_irqs = AB9540_NR_IRQS;
+ else if (is_ab8505(ab8500))
+ num_irqs = AB8505_NR_IRQS;
+ else
+ num_irqs = AB8500_NR_IRQS;
- for (irq = base; irq < base + AB8500_NR_IRQS; irq++) {
+ for (irq = base; irq < base + num_irqs; irq++) {
irq_set_chip_data(irq, ab8500);
irq_set_chip_and_handler(irq, &ab8500_irq_chip,
handle_simple_irq);
@@ -356,8 +393,16 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
{
int base = ab8500->irq_base;
int irq;
+ int num_irqs;
- for (irq = base; irq < base + AB8500_NR_IRQS; irq++) {
+ if (is_ab9540(ab8500))
+ num_irqs = AB9540_NR_IRQS;
+ else if (is_ab8505(ab8500))
+ num_irqs = AB8505_NR_IRQS;
+ else
+ num_irqs = AB8500_NR_IRQS;
+
+ for (irq = base; irq < base + num_irqs; irq++) {
#ifdef CONFIG_ARM
set_irq_flags(irq, 0);
#endif
@@ -366,6 +411,7 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
}
}
+/* AB8500 GPIO Resources */
static struct resource __devinitdata ab8500_gpio_resources[] = {
{
.name = "GPIO_INT6",
@@ -375,6 +421,28 @@ static struct resource __devinitdata ab8500_gpio_resources[] = {
}
};
+/* AB9540 GPIO Resources */
+static struct resource __devinitdata ab9540_gpio_resources[] = {
+ {
+ .name = "GPIO_INT6",
+ .start = AB8500_INT_GPIO6R,
+ .end = AB8500_INT_GPIO41F,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "GPIO_INT14",
+ .start = AB9540_INT_GPIO50R,
+ .end = AB9540_INT_GPIO54R,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "GPIO_INT15",
+ .start = AB9540_INT_GPIO50F,
+ .end = AB9540_INT_GPIO54F,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
static struct resource __devinitdata ab8500_gpadc_resources[] = {
{
.name = "HW_CONV_END",
@@ -491,12 +559,6 @@ static struct resource __devinitdata ab8500_charger_resources[] = {
.flags = IORESOURCE_IRQ,
},
{
- .name = "USB_CHARGE_DET_DONE",
- .start = AB8500_INT_USB_CHG_DET_DONE,
- .end = AB8500_INT_USB_CHG_DET_DONE,
- .flags = IORESOURCE_IRQ,
- },
- {
.name = "VBUS_OVV",
.start = AB8500_INT_VBUS_OVV,
.end = AB8500_INT_VBUS_OVV,
@@ -534,14 +596,8 @@ static struct resource __devinitdata ab8500_charger_resources[] = {
},
{
.name = "USB_CHARGER_NOT_OKR",
- .start = AB8500_INT_USB_CHARGER_NOT_OK,
- .end = AB8500_INT_USB_CHARGER_NOT_OK,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "USB_CHARGER_NOT_OKF",
- .start = AB8500_INT_USB_CHARGER_NOT_OKF,
- .end = AB8500_INT_USB_CHARGER_NOT_OKF,
+ .start = AB8500_INT_USB_CHARGER_NOT_OKR,
+ .end = AB8500_INT_USB_CHARGER_NOT_OKR,
.flags = IORESOURCE_IRQ,
},
{
@@ -616,6 +672,12 @@ static struct resource __devinitdata ab8500_fg_resources[] = {
.end = AB8500_INT_CC_INT_CALIB,
.flags = IORESOURCE_IRQ,
},
+ {
+ .name = "CCEOC",
+ .start = AB8500_INT_CCEOC,
+ .end = AB8500_INT_CCEOC,
+ .flags = IORESOURCE_IRQ,
+ },
};
static struct resource __devinitdata ab8500_chargalg_resources[] = {};
@@ -630,8 +692,8 @@ static struct resource __devinitdata ab8500_debug_resources[] = {
},
{
.name = "IRQ_LAST",
- .start = AB8500_INT_USB_CHARGER_NOT_OKF,
- .end = AB8500_INT_USB_CHARGER_NOT_OKF,
+ .start = AB8500_INT_XTAL32K_KO,
+ .end = AB8500_INT_XTAL32K_KO,
.flags = IORESOURCE_IRQ,
},
};
@@ -691,7 +753,7 @@ static struct resource __devinitdata ab8500_temp_resources[] = {
},
};
-static struct mfd_cell __devinitdata ab8500_devs[] = {
+static struct mfd_cell __devinitdata abx500_common_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
@@ -706,11 +768,6 @@ static struct mfd_cell __devinitdata ab8500_devs[] = {
.name = "ab8500-regulator",
},
{
- .name = "ab8500-gpio",
- .num_resources = ARRAY_SIZE(ab8500_gpio_resources),
- .resources = ab8500_gpio_resources,
- },
- {
.name = "ab8500-gpadc",
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
.resources = ab8500_gpadc_resources,
@@ -748,11 +805,7 @@ static struct mfd_cell __devinitdata ab8500_devs[] = {
{
.name = "ab8500-codec",
},
- {
- .name = "ab8500-usb",
- .num_resources = ARRAY_SIZE(ab8500_usb_resources),
- .resources = ab8500_usb_resources,
- },
+
{
.name = "ab8500-poweron-key",
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
@@ -781,6 +834,32 @@ static struct mfd_cell __devinitdata ab8500_devs[] = {
},
};
+static struct mfd_cell __devinitdata ab8500_devs[] = {
+ {
+ .name = "ab8500-gpio",
+ .num_resources = ARRAY_SIZE(ab8500_gpio_resources),
+ .resources = ab8500_gpio_resources,
+ },
+ {
+ .name = "ab8500-usb",
+ .num_resources = ARRAY_SIZE(ab8500_usb_resources),
+ .resources = ab8500_usb_resources,
+ },
+};
+
+static struct mfd_cell __devinitdata ab9540_devs[] = {
+ {
+ .name = "ab8500-gpio",
+ .num_resources = ARRAY_SIZE(ab9540_gpio_resources),
+ .resources = ab9540_gpio_resources,
+ },
+ {
+ .name = "ab9540-usb",
+ .num_resources = ARRAY_SIZE(ab8500_usb_resources),
+ .resources = ab8500_usb_resources,
+ },
+};
+
static ssize_t show_chip_id(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -842,9 +921,64 @@ static ssize_t show_turn_on_status(struct device *dev,
return sprintf(buf, "%#x\n", value);
}
+static ssize_t show_ab9540_dbbrstn(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ab8500 *ab8500;
+ int ret;
+ u8 value;
+
+ ab8500 = dev_get_drvdata(dev);
+
+ ret = get_register_interruptible(ab8500, AB8500_REGU_CTRL2,
+ AB9540_MODEM_CTRL2_REG, &value);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n",
+ (value & AB9540_MODEM_CTRL2_SWDBBRSTN_BIT) ? 1 : 0);
+}
+
+static ssize_t store_ab9540_dbbrstn(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ab8500 *ab8500;
+ int ret = count;
+ int err;
+ u8 bitvalues;
+
+ ab8500 = dev_get_drvdata(dev);
+
+ if (count > 0) {
+ switch (buf[0]) {
+ case '0':
+ bitvalues = 0;
+ break;
+ case '1':
+ bitvalues = AB9540_MODEM_CTRL2_SWDBBRSTN_BIT;
+ break;
+ default:
+ goto exit;
+ }
+
+ err = mask_and_set_register_interruptible(ab8500,
+ AB8500_REGU_CTRL2, AB9540_MODEM_CTRL2_REG,
+ AB9540_MODEM_CTRL2_SWDBBRSTN_BIT, bitvalues);
+ if (err)
+ dev_info(ab8500->dev,
+ "Failed to set DBBRSTN %c, err %#x\n",
+ buf[0], err);
+ }
+
+exit:
+ return ret;
+}
+
static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);
static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);
static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL);
+static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR,
+ show_ab9540_dbbrstn, store_ab9540_dbbrstn);
static struct attribute *ab8500_sysfs_entries[] = {
&dev_attr_chip_id.attr,
@@ -853,11 +987,23 @@ static struct attribute *ab8500_sysfs_entries[] = {
NULL,
};
+static struct attribute *ab9540_sysfs_entries[] = {
+ &dev_attr_chip_id.attr,
+ &dev_attr_switch_off_status.attr,
+ &dev_attr_turn_on_status.attr,
+ &dev_attr_dbbrstn.attr,
+ NULL,
+};
+
static struct attribute_group ab8500_attr_group = {
.attrs = ab8500_sysfs_entries,
};
-int __devinit ab8500_init(struct ab8500 *ab8500)
+static struct attribute_group ab9540_attr_group = {
+ .attrs = ab9540_sysfs_entries,
+};
+
+int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)
{
struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
int ret;
@@ -870,25 +1016,45 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
mutex_init(&ab8500->lock);
mutex_init(&ab8500->irq_lock);
+ if (version != AB8500_VERSION_UNDEFINED)
+ ab8500->version = version;
+ else {
+ ret = get_register_interruptible(ab8500, AB8500_MISC,
+ AB8500_IC_NAME_REG, &value);
+ if (ret < 0)
+ return ret;
+
+ ab8500->version = value;
+ }
+
ret = get_register_interruptible(ab8500, AB8500_MISC,
AB8500_REV_REG, &value);
if (ret < 0)
return ret;
- switch (value) {
- case AB8500_CUT1P0:
- case AB8500_CUT1P1:
- case AB8500_CUT2P0:
- case AB8500_CUT3P0:
- case AB8500_CUT3P3:
- dev_info(ab8500->dev, "detected chip, revision: %#x\n", value);
- break;
- default:
- dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value);
- return -EINVAL;
- }
ab8500->chip_id = value;
+ dev_info(ab8500->dev, "detected chip, %s rev. %1x.%1x\n",
+ ab8500_version_str[ab8500->version],
+ ab8500->chip_id >> 4,
+ ab8500->chip_id & 0x0F);
+
+ /* Configure AB8500 or AB9540 IRQ */
+ if (is_ab9540(ab8500) || is_ab8505(ab8500)) {
+ ab8500->mask_size = AB9540_NUM_IRQ_REGS;
+ ab8500->irq_reg_offset = ab9540_irq_regoffset;
+ } else {
+ ab8500->mask_size = AB8500_NUM_IRQ_REGS;
+ ab8500->irq_reg_offset = ab8500_irq_regoffset;
+ }
+ ab8500->mask = kzalloc(ab8500->mask_size, GFP_KERNEL);
+ if (!ab8500->mask)
+ return -ENOMEM;
+ ab8500->oldmask = kzalloc(ab8500->mask_size, GFP_KERNEL);
+ if (!ab8500->oldmask) {
+ ret = -ENOMEM;
+ goto out_freemask;
+ }
/*
* ab8500 has switched off due to (SWITCH_OFF_STATUS):
* 0x01 Swoff bit programming
@@ -911,30 +1077,33 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
plat->init(ab8500);
/* Clear and mask all interrupts */
- for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
- /* Interrupt register 12 doesn't exist prior to version 2.0 */
- if (ab8500_irq_regoffset[i] == 11 &&
- ab8500->chip_id < AB8500_CUT2P0)
+ for (i = 0; i < ab8500->mask_size; i++) {
+ /*
+ * Interrupt register 12 doesn't exist prior to AB8500 version
+ * 2.0
+ */
+ if (ab8500->irq_reg_offset[i] == 11 &&
+ is_ab8500_1p1_or_earlier(ab8500))
continue;
get_register_interruptible(ab8500, AB8500_INTERRUPT,
- AB8500_IT_LATCH1_REG + ab8500_irq_regoffset[i],
+ AB8500_IT_LATCH1_REG + ab8500->irq_reg_offset[i],
&value);
set_register_interruptible(ab8500, AB8500_INTERRUPT,
- AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i], 0xff);
+ AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i], 0xff);
}
ret = abx500_register_ops(ab8500->dev, &ab8500_ops);
if (ret)
- return ret;
+ goto out_freeoldmask;
- for (i = 0; i < AB8500_NUM_IRQ_REGS; i++)
+ for (i = 0; i < ab8500->mask_size; i++)
ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
if (ab8500->irq_base) {
ret = ab8500_irq_init(ab8500);
if (ret)
- return ret;
+ goto out_freeoldmask;
ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq,
IRQF_ONESHOT | IRQF_NO_SUSPEND,
@@ -943,17 +1112,34 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
goto out_removeirq;
}
- ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
- ARRAY_SIZE(ab8500_devs), NULL,
+ ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
+ ARRAY_SIZE(abx500_common_devs), NULL,
ab8500->irq_base);
+
if (ret)
goto out_freeirq;
- ret = sysfs_create_group(&ab8500->dev->kobj, &ab8500_attr_group);
+ if (is_ab9540(ab8500))
+ ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
+ ARRAY_SIZE(ab9540_devs), NULL,
+ ab8500->irq_base);
+ else
+ ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
+ ARRAY_SIZE(ab9540_devs), NULL,
+ ab8500->irq_base);
if (ret)
- dev_err(ab8500->dev, "error creating sysfs entries\n");
+ goto out_freeirq;
- return ret;
+ if (is_ab9540(ab8500))
+ ret = sysfs_create_group(&ab8500->dev->kobj,
+ &ab9540_attr_group);
+ else
+ ret = sysfs_create_group(&ab8500->dev->kobj,
+ &ab8500_attr_group);
+ if (ret)
+ dev_err(ab8500->dev, "error creating sysfs entries\n");
+ else
+ return ret;
out_freeirq:
if (ab8500->irq_base)
@@ -961,18 +1147,27 @@ out_freeirq:
out_removeirq:
if (ab8500->irq_base)
ab8500_irq_remove(ab8500);
+out_freeoldmask:
+ kfree(ab8500->oldmask);
+out_freemask:
+ kfree(ab8500->mask);
return ret;
}
int __devexit ab8500_exit(struct ab8500 *ab8500)
{
- sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
+ if (is_ab9540(ab8500))
+ sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group);
+ else
+ sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
mfd_remove_devices(ab8500->dev);
if (ab8500->irq_base) {
free_irq(ab8500->irq, ab8500);
ab8500_irq_remove(ab8500);
}
+ kfree(ab8500->oldmask);
+ kfree(ab8500->mask);
return 0;
}
diff --git a/drivers/mfd/ab8500-i2c.c b/drivers/mfd/ab8500-i2c.c
index 087fecd..b83045f 100644
--- a/drivers/mfd/ab8500-i2c.c
+++ b/drivers/mfd/ab8500-i2c.c
@@ -11,7 +11,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/db8500-prcmu.h>
+#include <linux/mfd/dbx500-prcmu.h>
static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
{
@@ -23,6 +23,18 @@ static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
return ret;
}
+static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
+ u8 data)
+{
+ int ret;
+
+ ret = prcmu_abb_write_masked((u8)(addr >> 8), (u8)(addr & 0xFF), &data,
+ &mask, 1);
+ if (ret < 0)
+ dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
+ return ret;
+}
+
static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
{
int ret;
@@ -38,6 +50,7 @@ static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
static int __devinit ab8500_i2c_probe(struct platform_device *plf)
{
+ const struct platform_device_id *platid = platform_get_device_id(plf);
struct ab8500 *ab8500;
struct resource *resource;
int ret;
@@ -58,13 +71,15 @@ static int __devinit ab8500_i2c_probe(struct platform_device *plf)
ab8500->read = ab8500_i2c_read;
ab8500->write = ab8500_i2c_write;
+ ab8500->write_masked = ab8500_i2c_write_masked;
platform_set_drvdata(plf, ab8500);
- ret = ab8500_init(ab8500);
+ ret = ab8500_init(ab8500, platid->driver_data);
if (ret)
kfree(ab8500);
+
return ret;
}
@@ -78,13 +93,22 @@ static int __devexit ab8500_i2c_remove(struct platform_device *plf)
return 0;
}
+static const struct platform_device_id ab8500_id[] = {
+ { "ab8500-i2c", AB8500_VERSION_AB8500 },
+ { "ab8505-i2c", AB8500_VERSION_AB8505 },
+ { "ab9540-i2c", AB8500_VERSION_AB9540 },
+ { "ab8540-i2c", AB8500_VERSION_AB8540 },
+ { }
+};
+
static struct platform_driver ab8500_i2c_driver = {
.driver = {
.name = "ab8500-i2c",
.owner = THIS_MODULE,
},
.probe = ab8500_i2c_probe,
- .remove = __devexit_p(ab8500_i2c_remove)
+ .remove = __devexit_p(ab8500_i2c_remove),
+ .id_table = ab8500_id,
};
static int __init ab8500_i2c_init(void)
diff --git a/drivers/mfd/anatop-mfd.c b/drivers/mfd/anatop-mfd.c
new file mode 100644
index 0000000..2af4248
--- /dev/null
+++ b/drivers/mfd/anatop-mfd.c
@@ -0,0 +1,137 @@
+/*
+ * Anatop MFD driver
+ *
+ * Copyright (C) 2012 Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
+ * Copyright (C) 2012 Linaro
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/mfd/anatop.h>
+
+u32 anatop_get_bits(struct anatop *adata, u32 addr, int bit_shift,
+ int bit_width)
+{
+ u32 val, mask;
+
+ if (bit_width == 32)
+ mask = ~0;
+ else
+ mask = (1 << bit_width) - 1;
+
+ val = readl(adata->ioreg + addr);
+ val = (val >> bit_shift) & mask;
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(anatop_get_bits);
+
+void anatop_set_bits(struct anatop *adata, u32 addr, int bit_shift,
+ int bit_width, u32 data)
+{
+ u32 val, mask;
+
+ if (bit_width == 32)
+ mask = ~0;
+ else
+ mask = (1 << bit_width) - 1;
+
+ spin_lock(&adata->reglock);
+ val = readl(adata->ioreg + addr) & ~(mask << bit_shift);
+ writel((data << bit_shift) | val, adata->ioreg + addr);
+ spin_unlock(&adata->reglock);
+}
+EXPORT_SYMBOL_GPL(anatop_set_bits);
+
+static const struct of_device_id of_anatop_match[] = {
+ { .compatible = "fsl,imx6q-anatop", },
+ { },
+};
+
+static int __devinit of_anatop_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ void *ioreg;
+ struct anatop *drvdata;
+
+ ioreg = of_iomap(np, 0);
+ if (!ioreg)
+ return -EADDRNOTAVAIL;
+ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+ drvdata->ioreg = ioreg;
+ spin_lock_init(&drvdata->reglock);
+ platform_set_drvdata(pdev, drvdata);
+ of_platform_populate(np, of_anatop_match, NULL, dev);
+
+ return 0;
+}
+
+static int __devexit of_anatop_remove(struct platform_device *pdev)
+{
+ struct anatop *drvdata;
+ drvdata = platform_get_drvdata(pdev);
+ iounmap(drvdata->ioreg);
+
+ return 0;
+}
+
+static struct platform_driver anatop_of_driver = {
+ .driver = {
+ .name = "anatop-mfd",
+ .owner = THIS_MODULE,
+ .of_match_table = of_anatop_match,
+ },
+ .probe = of_anatop_probe,
+ .remove = of_anatop_remove,
+};
+
+static int __init anatop_init(void)
+{
+ return platform_driver_register(&anatop_of_driver);
+}
+postcore_initcall(anatop_init);
+
+static void __exit anatop_exit(void)
+{
+ platform_driver_unregister(&anatop_of_driver);
+}
+module_exit(anatop_exit);
+
+MODULE_AUTHOR("Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>");
+MODULE_DESCRIPTION("ANATOP MFD driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index b85bbd7..1895cf9 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -525,6 +525,11 @@ static void asic3_gpio_set(struct gpio_chip *chip,
return;
}
+static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ return (offset < ASIC3_NUM_GPIOS) ? IRQ_BOARD_START + offset : -ENXIO;
+}
+
static __init int asic3_gpio_probe(struct platform_device *pdev,
u16 *gpio_config, int num)
{
@@ -976,6 +981,7 @@ static int __init asic3_probe(struct platform_device *pdev)
asic->gpio.set = asic3_gpio_set;
asic->gpio.direction_input = asic3_gpio_direction_input;
asic->gpio.direction_output = asic3_gpio_direction_output;
+ asic->gpio.to_irq = asic3_gpio_to_irq;
ret = asic3_gpio_probe(pdev,
pdata->gpio_config,
diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c
index 5ddde2a..7ff313f 100644
--- a/drivers/mfd/da9052-core.c
+++ b/drivers/mfd/da9052-core.c
@@ -16,7 +16,6 @@
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
-#include <linux/mutex.h>
#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -647,8 +646,6 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
struct irq_desc *desc;
int ret;
- mutex_init(&da9052->io_lock);
-
if (pdata && pdata->init != NULL)
pdata->init(da9052);
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
index 44b97c7..36b88e3 100644
--- a/drivers/mfd/da9052-i2c.c
+++ b/drivers/mfd/da9052-i2c.c
@@ -74,24 +74,27 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client,
ret = da9052_i2c_enable_multiwrite(da9052);
if (ret < 0)
- goto err;
+ goto err_regmap;
ret = da9052_device_init(da9052, id->driver_data);
if (ret != 0)
- goto err;
+ goto err_regmap;
return 0;
+err_regmap:
+ regmap_exit(da9052->regmap);
err:
kfree(da9052);
return ret;
}
-static int da9052_i2c_remove(struct i2c_client *client)
+static int __devexit da9052_i2c_remove(struct i2c_client *client)
{
struct da9052 *da9052 = i2c_get_clientdata(client);
da9052_device_exit(da9052);
+ regmap_exit(da9052->regmap);
kfree(da9052);
return 0;
@@ -107,7 +110,7 @@ static struct i2c_device_id da9052_i2c_id[] = {
static struct i2c_driver da9052_i2c_driver = {
.probe = da9052_i2c_probe,
- .remove = da9052_i2c_remove,
+ .remove = __devexit_p(da9052_i2c_remove),
.id_table = da9052_i2c_id,
.driver = {
.name = "da9052",
diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c
index cdbc7ca..6faf149 100644
--- a/drivers/mfd/da9052-spi.c
+++ b/drivers/mfd/da9052-spi.c
@@ -21,7 +21,7 @@
#include <linux/mfd/da9052/da9052.h>
-static int da9052_spi_probe(struct spi_device *spi)
+static int __devinit da9052_spi_probe(struct spi_device *spi)
{
int ret;
const struct spi_device_id *id = spi_get_device_id(spi);
@@ -52,20 +52,23 @@ static int da9052_spi_probe(struct spi_device *spi)
ret = da9052_device_init(da9052, id->driver_data);
if (ret != 0)
- goto err;
+ goto err_regmap;
return 0;
+err_regmap:
+ regmap_exit(da9052->regmap);
err:
kfree(da9052);
return ret;
}
-static int da9052_spi_remove(struct spi_device *spi)
+static int __devexit da9052_spi_remove(struct spi_device *spi)
{
struct da9052 *da9052 = dev_get_drvdata(&spi->dev);
da9052_device_exit(da9052);
+ regmap_exit(da9052->regmap);
kfree(da9052);
return 0;
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index af8e0ef..ebc1e86 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -30,6 +30,7 @@
#include <linux/mfd/dbx500-prcmu.h>
#include <linux/regulator/db8500-prcmu.h>
#include <linux/regulator/machine.h>
+#include <asm/hardware/gic.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/db8500-regs.h>
@@ -39,11 +40,6 @@
/* Offset for the firmware version within the TCPM */
#define PRCMU_FW_VERSION_OFFSET 0xA4
-/* PRCMU project numbers, defined by PRCMU FW */
-#define PRCMU_PROJECT_ID_8500V1_0 1
-#define PRCMU_PROJECT_ID_8500V2_0 2
-#define PRCMU_PROJECT_ID_8400V2_0 3
-
/* Index of different voltages to be used when accessing AVSData */
#define PRCM_AVS_BASE 0x2FC
#define PRCM_AVS_VBB_RET (PRCM_AVS_BASE + 0x0)
@@ -137,6 +133,8 @@
#define PRCM_REQ_MB1_ARM_OPP (PRCM_REQ_MB1 + 0x0)
#define PRCM_REQ_MB1_APE_OPP (PRCM_REQ_MB1 + 0x1)
#define PRCM_REQ_MB1_PLL_ON_OFF (PRCM_REQ_MB1 + 0x4)
+#define PLL_SOC0_OFF 0x1
+#define PLL_SOC0_ON 0x2
#define PLL_SOC1_OFF 0x4
#define PLL_SOC1_ON 0x8
@@ -266,6 +264,11 @@
#define WAKEUP_BIT_GPIO7 BIT(30)
#define WAKEUP_BIT_GPIO8 BIT(31)
+static struct {
+ bool valid;
+ struct prcmu_fw_version version;
+} fw_info;
+
/*
* This vector maps irq numbers to the bits in the bit field used in
* communication with the PRCMU firmware.
@@ -341,11 +344,13 @@ static struct {
* mb1_transfer - state needed for mailbox 1 communication.
* @lock: The transaction lock.
* @work: The transaction completion structure.
+ * @ape_opp: The current APE OPP.
* @ack: Reply ("acknowledge") data.
*/
static struct {
struct mutex lock;
struct completion work;
+ u8 ape_opp;
struct {
u8 header;
u8 arm_opp;
@@ -413,79 +418,102 @@ static struct {
static atomic_t ac_wake_req_state = ATOMIC_INIT(0);
/* Spinlocks */
+static DEFINE_SPINLOCK(prcmu_lock);
static DEFINE_SPINLOCK(clkout_lock);
-static DEFINE_SPINLOCK(gpiocr_lock);
/* Global var to runtime determine TCDM base for v2 or v1 */
static __iomem void *tcdm_base;
struct clk_mgt {
- unsigned int offset;
+ void __iomem *reg;
u32 pllsw;
+ int branch;
+ bool clk38div;
+};
+
+enum {
+ PLL_RAW,
+ PLL_FIX,
+ PLL_DIV
};
static DEFINE_SPINLOCK(clk_mgt_lock);
-#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { (PRCM_##_name##_MGT_OFF), 0 }
+#define CLK_MGT_ENTRY(_name, _branch, _clk38div)[PRCMU_##_name] = \
+ { (PRCM_##_name##_MGT), 0 , _branch, _clk38div}
struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
- CLK_MGT_ENTRY(SGACLK),
- CLK_MGT_ENTRY(UARTCLK),
- CLK_MGT_ENTRY(MSP02CLK),
- CLK_MGT_ENTRY(MSP1CLK),
- CLK_MGT_ENTRY(I2CCLK),
- CLK_MGT_ENTRY(SDMMCCLK),
- CLK_MGT_ENTRY(SLIMCLK),
- CLK_MGT_ENTRY(PER1CLK),
- CLK_MGT_ENTRY(PER2CLK),
- CLK_MGT_ENTRY(PER3CLK),
- CLK_MGT_ENTRY(PER5CLK),
- CLK_MGT_ENTRY(PER6CLK),
- CLK_MGT_ENTRY(PER7CLK),
- CLK_MGT_ENTRY(LCDCLK),
- CLK_MGT_ENTRY(BMLCLK),
- CLK_MGT_ENTRY(HSITXCLK),
- CLK_MGT_ENTRY(HSIRXCLK),
- CLK_MGT_ENTRY(HDMICLK),
- CLK_MGT_ENTRY(APEATCLK),
- CLK_MGT_ENTRY(APETRACECLK),
- CLK_MGT_ENTRY(MCDECLK),
- CLK_MGT_ENTRY(IPI2CCLK),
- CLK_MGT_ENTRY(DSIALTCLK),
- CLK_MGT_ENTRY(DMACLK),
- CLK_MGT_ENTRY(B2R2CLK),
- CLK_MGT_ENTRY(TVCLK),
- CLK_MGT_ENTRY(SSPCLK),
- CLK_MGT_ENTRY(RNGCLK),
- CLK_MGT_ENTRY(UICCCLK),
+ CLK_MGT_ENTRY(SGACLK, PLL_DIV, false),
+ CLK_MGT_ENTRY(UARTCLK, PLL_FIX, true),
+ CLK_MGT_ENTRY(MSP02CLK, PLL_FIX, true),
+ CLK_MGT_ENTRY(MSP1CLK, PLL_FIX, true),
+ CLK_MGT_ENTRY(I2CCLK, PLL_FIX, true),
+ CLK_MGT_ENTRY(SDMMCCLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(SLIMCLK, PLL_FIX, true),
+ CLK_MGT_ENTRY(PER1CLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(PER2CLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(PER3CLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(PER5CLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(PER6CLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(PER7CLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(LCDCLK, PLL_FIX, true),
+ CLK_MGT_ENTRY(BMLCLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(HSITXCLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(HSIRXCLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(HDMICLK, PLL_FIX, false),
+ CLK_MGT_ENTRY(APEATCLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(APETRACECLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(MCDECLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(IPI2CCLK, PLL_FIX, true),
+ CLK_MGT_ENTRY(DSIALTCLK, PLL_FIX, false),
+ CLK_MGT_ENTRY(DMACLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(B2R2CLK, PLL_DIV, true),
+ CLK_MGT_ENTRY(TVCLK, PLL_FIX, true),
+ CLK_MGT_ENTRY(SSPCLK, PLL_FIX, true),
+ CLK_MGT_ENTRY(RNGCLK, PLL_FIX, true),
+ CLK_MGT_ENTRY(UICCCLK, PLL_FIX, false),
+};
+
+struct dsiclk {
+ u32 divsel_mask;
+ u32 divsel_shift;
+ u32 divsel;
+};
+
+static struct dsiclk dsiclk[2] = {
+ {
+ .divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_MASK,
+ .divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_SHIFT,
+ .divsel = PRCM_DSI_PLLOUT_SEL_PHI,
+ },
+ {
+ .divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_MASK,
+ .divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_SHIFT,
+ .divsel = PRCM_DSI_PLLOUT_SEL_PHI,
+ }
};
-static struct regulator *hwacc_regulator[NUM_HW_ACC];
-static struct regulator *hwacc_ret_regulator[NUM_HW_ACC];
-
-static bool hwacc_enabled[NUM_HW_ACC];
-static bool hwacc_ret_enabled[NUM_HW_ACC];
-
-static const char *hwacc_regulator_name[NUM_HW_ACC] = {
- [HW_ACC_SVAMMDSP] = "hwacc-sva-mmdsp",
- [HW_ACC_SVAPIPE] = "hwacc-sva-pipe",
- [HW_ACC_SIAMMDSP] = "hwacc-sia-mmdsp",
- [HW_ACC_SIAPIPE] = "hwacc-sia-pipe",
- [HW_ACC_SGA] = "hwacc-sga",
- [HW_ACC_B2R2] = "hwacc-b2r2",
- [HW_ACC_MCDE] = "hwacc-mcde",
- [HW_ACC_ESRAM1] = "hwacc-esram1",
- [HW_ACC_ESRAM2] = "hwacc-esram2",
- [HW_ACC_ESRAM3] = "hwacc-esram3",
- [HW_ACC_ESRAM4] = "hwacc-esram4",
+struct dsiescclk {
+ u32 en;
+ u32 div_mask;
+ u32 div_shift;
};
-static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = {
- [HW_ACC_SVAMMDSP] = "hwacc-sva-mmdsp-ret",
- [HW_ACC_SIAMMDSP] = "hwacc-sia-mmdsp-ret",
- [HW_ACC_ESRAM1] = "hwacc-esram1-ret",
- [HW_ACC_ESRAM2] = "hwacc-esram2-ret",
- [HW_ACC_ESRAM3] = "hwacc-esram3-ret",
- [HW_ACC_ESRAM4] = "hwacc-esram4-ret",
+static struct dsiescclk dsiescclk[3] = {
+ {
+ .en = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_EN,
+ .div_mask = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_MASK,
+ .div_shift = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_SHIFT,
+ },
+ {
+ .en = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_EN,
+ .div_mask = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_MASK,
+ .div_shift = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_SHIFT,
+ },
+ {
+ .en = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_EN,
+ .div_mask = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_MASK,
+ .div_shift = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_SHIFT,
+ }
};
/*
@@ -503,9 +531,6 @@ static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = {
/* PLLDIV=12, PLLSW=4 (PLLDDR) */
#define PRCMU_DSI_CLOCK_SETTING 0x0000008C
-/* PLLDIV=8, PLLSW=4 (PLLDDR) */
-#define PRCMU_DSI_CLOCK_SETTING_U8400 0x00000088
-
/* DPI 50000000 Hz */
#define PRCMU_DPI_CLOCK_SETTING ((1 << PRCMU_CLK_PLL_SW_SHIFT) | \
(16 << PRCMU_CLK_PLL_DIV_SHIFT))
@@ -514,9 +539,6 @@ static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = {
/* D=101, N=1, R=4, SELDIV2=0 */
#define PRCMU_PLLDSI_FREQ_SETTING 0x00040165
-/* D=70, N=1, R=3, SELDIV2=0 */
-#define PRCMU_PLLDSI_FREQ_SETTING_U8400 0x00030146
-
#define PRCMU_ENABLE_PLLDSI 0x00000001
#define PRCMU_DISABLE_PLLDSI 0x00000000
#define PRCMU_RELEASE_RESET_DSS 0x0000400C
@@ -528,30 +550,17 @@ static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = {
#define PRCMU_PLLDSI_LOCKP_LOCKED 0x3
-static struct {
- u8 project_number;
- u8 api_version;
- u8 func_version;
- u8 errata;
-} prcmu_version;
-
-
int db8500_prcmu_enable_dsipll(void)
{
int i;
- unsigned int plldsifreq;
/* Clear DSIPLL_RESETN */
writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR);
/* Unclamp DSIPLL in/out */
writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR);
- if (prcmu_is_u8400())
- plldsifreq = PRCMU_PLLDSI_FREQ_SETTING_U8400;
- else
- plldsifreq = PRCMU_PLLDSI_FREQ_SETTING;
/* Set DSI PLL FREQ */
- writel(plldsifreq, PRCM_PLLDSI_FREQ);
+ writel(PRCMU_PLLDSI_FREQ_SETTING, PRCM_PLLDSI_FREQ);
writel(PRCMU_DSI_PLLOUT_SEL_SETTING, PRCM_DSI_PLLOUT_SEL);
/* Enable Escape clocks */
writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
@@ -583,12 +592,6 @@ int db8500_prcmu_disable_dsipll(void)
int db8500_prcmu_set_display_clocks(void)
{
unsigned long flags;
- unsigned int dsiclk;
-
- if (prcmu_is_u8400())
- dsiclk = PRCMU_DSI_CLOCK_SETTING_U8400;
- else
- dsiclk = PRCMU_DSI_CLOCK_SETTING;
spin_lock_irqsave(&clk_mgt_lock, flags);
@@ -596,7 +599,7 @@ int db8500_prcmu_set_display_clocks(void)
while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
cpu_relax();
- writel(dsiclk, PRCM_HDMICLK_MGT);
+ writel(PRCMU_DSI_CLOCK_SETTING, PRCM_HDMICLK_MGT);
writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT);
writel(PRCMU_DPI_CLOCK_SETTING, PRCM_LCDCLK_MGT);
@@ -608,43 +611,41 @@ int db8500_prcmu_set_display_clocks(void)
return 0;
}
-/**
- * prcmu_enable_spi2 - Enables pin muxing for SPI2 on OtherAlternateC1.
- */
-void prcmu_enable_spi2(void)
+u32 db8500_prcmu_read(unsigned int reg)
+{
+ return readl(_PRCMU_BASE + reg);
+}
+
+void db8500_prcmu_write(unsigned int reg, u32 value)
{
- u32 reg;
unsigned long flags;
- spin_lock_irqsave(&gpiocr_lock, flags);
- reg = readl(PRCM_GPIOCR);
- writel(reg | PRCM_GPIOCR_SPI2_SELECT, PRCM_GPIOCR);
- spin_unlock_irqrestore(&gpiocr_lock, flags);
+ spin_lock_irqsave(&prcmu_lock, flags);
+ writel(value, (_PRCMU_BASE + reg));
+ spin_unlock_irqrestore(&prcmu_lock, flags);
}
-/**
- * prcmu_disable_spi2 - Disables pin muxing for SPI2 on OtherAlternateC1.
- */
-void prcmu_disable_spi2(void)
+void db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value)
{
- u32 reg;
+ u32 val;
unsigned long flags;
- spin_lock_irqsave(&gpiocr_lock, flags);
- reg = readl(PRCM_GPIOCR);
- writel(reg & ~PRCM_GPIOCR_SPI2_SELECT, PRCM_GPIOCR);
- spin_unlock_irqrestore(&gpiocr_lock, flags);
+ spin_lock_irqsave(&prcmu_lock, flags);
+ val = readl(_PRCMU_BASE + reg);
+ val = ((val & ~mask) | (value & mask));
+ writel(val, (_PRCMU_BASE + reg));
+ spin_unlock_irqrestore(&prcmu_lock, flags);
}
-bool prcmu_has_arm_maxopp(void)
+struct prcmu_fw_version *prcmu_get_fw_version(void)
{
- return (readb(tcdm_base + PRCM_AVS_VARM_MAX_OPP) &
- PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK;
+ return fw_info.valid ? &fw_info.version : NULL;
}
-bool prcmu_is_u8400(void)
+bool prcmu_has_arm_maxopp(void)
{
- return prcmu_version.project_number == PRCMU_PROJECT_ID_8400V2_0;
+ return (readb(tcdm_base + PRCM_AVS_VARM_MAX_OPP) &
+ PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK;
}
/**
@@ -787,6 +788,124 @@ int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
return 0;
}
+u8 db8500_prcmu_get_power_state_result(void)
+{
+ return readb(tcdm_base + PRCM_ACK_MB0_AP_PWRSTTR_STATUS);
+}
+
+/* This function decouple the gic from the prcmu */
+int db8500_prcmu_gic_decouple(void)
+{
+ u32 val = readl(PRCM_A9_MASK_REQ);
+
+ /* Set bit 0 register value to 1 */
+ writel(val | PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ,
+ PRCM_A9_MASK_REQ);
+
+ /* Make sure the register is updated */
+ readl(PRCM_A9_MASK_REQ);
+
+ /* Wait a few cycles for the gic mask completion */
+ udelay(1);
+
+ return 0;
+}
+
+/* This function recouple the gic with the prcmu */
+int db8500_prcmu_gic_recouple(void)
+{
+ u32 val = readl(PRCM_A9_MASK_REQ);
+
+ /* Set bit 0 register value to 0 */
+ writel(val & ~PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ, PRCM_A9_MASK_REQ);
+
+ return 0;
+}
+
+#define PRCMU_GIC_NUMBER_REGS 5
+
+/*
+ * This function checks if there are pending irq on the gic. It only
+ * makes sense if the gic has been decoupled before with the
+ * db8500_prcmu_gic_decouple function. Disabling an interrupt only
+ * disables the forwarding of the interrupt to any CPU interface. It
+ * does not prevent the interrupt from changing state, for example
+ * becoming pending, or active and pending if it is already
+ * active. Hence, we have to check the interrupt is pending *and* is
+ * active.
+ */
+bool db8500_prcmu_gic_pending_irq(void)
+{
+ u32 pr; /* Pending register */
+ u32 er; /* Enable register */
+ void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE);
+ int i;
+
+ /* 5 registers. STI & PPI not skipped */
+ for (i = 0; i < PRCMU_GIC_NUMBER_REGS; i++) {
+
+ pr = readl_relaxed(dist_base + GIC_DIST_PENDING_SET + i * 4);
+ er = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+ if (pr & er)
+ return true; /* There is a pending interrupt */
+ }
+
+ return false;
+}
+
+/*
+ * This function checks if there are pending interrupt on the
+ * prcmu which has been delegated to monitor the irqs with the
+ * db8500_prcmu_copy_gic_settings function.
+ */
+bool db8500_prcmu_pending_irq(void)
+{
+ u32 it, im;
+ int i;
+
+ for (i = 0; i < PRCMU_GIC_NUMBER_REGS - 1; i++) {
+ it = readl(PRCM_ARMITVAL31TO0 + i * 4);
+ im = readl(PRCM_ARMITMSK31TO0 + i * 4);
+ if (it & im)
+ return true; /* There is a pending interrupt */
+ }
+
+ return false;
+}
+
+/*
+ * This function checks if the specified cpu is in in WFI. It's usage
+ * makes sense only if the gic is decoupled with the db8500_prcmu_gic_decouple
+ * function. Of course passing smp_processor_id() to this function will
+ * always return false...
+ */
+bool db8500_prcmu_is_cpu_in_wfi(int cpu)
+{
+ return readl(PRCM_ARM_WFI_STANDBY) & cpu ? PRCM_ARM_WFI_STANDBY_WFI1 :
+ PRCM_ARM_WFI_STANDBY_WFI0;
+}
+
+/*
+ * This function copies the gic SPI settings to the prcmu in order to
+ * monitor them and abort/finish the retention/off sequence or state.
+ */
+int db8500_prcmu_copy_gic_settings(void)
+{
+ u32 er; /* Enable register */
+ void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE);
+ int i;
+
+ /* We skip the STI and PPI */
+ for (i = 0; i < PRCMU_GIC_NUMBER_REGS - 1; i++) {
+ er = readl_relaxed(dist_base +
+ GIC_DIST_ENABLE_SET + (i + 1) * 4);
+ writel(er, PRCM_ARMITMSK31TO0 + i * 4);
+ }
+
+ return 0;
+}
+
/* This function should only be called while mb0_transfer.lock is held. */
static void config_wakeups(void)
{
@@ -909,23 +1028,23 @@ int db8500_prcmu_get_arm_opp(void)
}
/**
- * prcmu_get_ddr_opp - get the current DDR OPP
+ * db8500_prcmu_get_ddr_opp - get the current DDR OPP
*
* Returns: the current DDR OPP
*/
-int prcmu_get_ddr_opp(void)
+int db8500_prcmu_get_ddr_opp(void)
{
return readb(PRCM_DDR_SUBSYS_APE_MINBW);
}
/**
- * set_ddr_opp - set the appropriate DDR OPP
+ * db8500_set_ddr_opp - set the appropriate DDR OPP
* @opp: The new DDR operating point to which transition is to be made
* Returns: 0 on success, non-zero on failure
*
* This function sets the operating point of the DDR.
*/
-int prcmu_set_ddr_opp(u8 opp)
+int db8500_prcmu_set_ddr_opp(u8 opp)
{
if (opp < DDR_100_OPP || opp > DDR_25_OPP)
return -EINVAL;
@@ -935,25 +1054,82 @@ int prcmu_set_ddr_opp(u8 opp)
return 0;
}
+
+/* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */
+static void request_even_slower_clocks(bool enable)
+{
+ void __iomem *clock_reg[] = {
+ PRCM_ACLK_MGT,
+ PRCM_DMACLK_MGT
+ };
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ for (i = 0; i < ARRAY_SIZE(clock_reg); i++) {
+ u32 val;
+ u32 div;
+
+ val = readl(clock_reg[i]);
+ div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK);
+ if (enable) {
+ if ((div <= 1) || (div > 15)) {
+ pr_err("prcmu: Bad clock divider %d in %s\n",
+ div, __func__);
+ goto unlock_and_return;
+ }
+ div <<= 1;
+ } else {
+ if (div <= 2)
+ goto unlock_and_return;
+ div >>= 1;
+ }
+ val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) |
+ (div & PRCM_CLK_MGT_CLKPLLDIV_MASK));
+ writel(val, clock_reg[i]);
+ }
+
+unlock_and_return:
+ /* Release the HW semaphore. */
+ writel(0, PRCM_SEM);
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+}
+
/**
- * set_ape_opp - set the appropriate APE OPP
+ * db8500_set_ape_opp - set the appropriate APE OPP
* @opp: The new APE operating point to which transition is to be made
* Returns: 0 on success, non-zero on failure
*
* This function sets the operating point of the APE.
*/
-int prcmu_set_ape_opp(u8 opp)
+int db8500_prcmu_set_ape_opp(u8 opp)
{
int r = 0;
+ if (opp == mb1_transfer.ape_opp)
+ return 0;
+
mutex_lock(&mb1_transfer.lock);
+ if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)
+ request_even_slower_clocks(false);
+
+ if ((opp != APE_100_OPP) && (mb1_transfer.ape_opp != APE_100_OPP))
+ goto skip_message;
+
while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
- writeb(opp, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
+ writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp),
+ (tcdm_base + PRCM_REQ_MB1_APE_OPP));
writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
@@ -962,17 +1138,24 @@ int prcmu_set_ape_opp(u8 opp)
(mb1_transfer.ack.ape_opp != opp))
r = -EIO;
+skip_message:
+ if ((!r && (opp == APE_50_PARTLY_25_OPP)) ||
+ (r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)))
+ request_even_slower_clocks(true);
+ if (!r)
+ mb1_transfer.ape_opp = opp;
+
mutex_unlock(&mb1_transfer.lock);
return r;
}
/**
- * prcmu_get_ape_opp - get the current APE OPP
+ * db8500_prcmu_get_ape_opp - get the current APE OPP
*
* Returns: the current APE OPP
*/
-int prcmu_get_ape_opp(void)
+int db8500_prcmu_get_ape_opp(void)
{
return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_APE_OPP);
}
@@ -1056,7 +1239,9 @@ static int request_pll(u8 clock, bool enable)
{
int r = 0;
- if (clock == PRCMU_PLLSOC1)
+ if (clock == PRCMU_PLLSOC0)
+ clock = (enable ? PLL_SOC0_ON : PLL_SOC0_OFF);
+ else if (clock == PRCMU_PLLSOC1)
clock = (enable ? PLL_SOC1_ON : PLL_SOC1_OFF);
else
return -EINVAL;
@@ -1081,132 +1266,6 @@ static int request_pll(u8 clock, bool enable)
}
/**
- * prcmu_set_hwacc - set the power state of a h/w accelerator
- * @hwacc_dev: The hardware accelerator (enum hw_acc_dev).
- * @state: The new power state (enum hw_acc_state).
- *
- * This function sets the power state of a hardware accelerator.
- * This function should not be called from interrupt context.
- *
- * NOTE! Deprecated, to be removed when all users switched over to use the
- * regulator framework API.
- */
-int prcmu_set_hwacc(u16 hwacc_dev, u8 state)
-{
- int r = 0;
- bool ram_retention = false;
- bool enable, enable_ret;
-
- /* check argument */
- BUG_ON(hwacc_dev >= NUM_HW_ACC);
-
- /* get state of switches */
- enable = hwacc_enabled[hwacc_dev];
- enable_ret = hwacc_ret_enabled[hwacc_dev];
-
- /* set flag if retention is possible */
- switch (hwacc_dev) {
- case HW_ACC_SVAMMDSP:
- case HW_ACC_SIAMMDSP:
- case HW_ACC_ESRAM1:
- case HW_ACC_ESRAM2:
- case HW_ACC_ESRAM3:
- case HW_ACC_ESRAM4:
- ram_retention = true;
- break;
- }
-
- /* check argument */
- BUG_ON(state > HW_ON);
- BUG_ON(state == HW_OFF_RAMRET && !ram_retention);
-
- /* modify enable flags */
- switch (state) {
- case HW_OFF:
- enable_ret = false;
- enable = false;
- break;
- case HW_ON:
- enable = true;
- break;
- case HW_OFF_RAMRET:
- enable_ret = true;
- enable = false;
- break;
- }
-
- /* get regulator (lazy) */
- if (hwacc_regulator[hwacc_dev] == NULL) {
- hwacc_regulator[hwacc_dev] = regulator_get(NULL,
- hwacc_regulator_name[hwacc_dev]);
- if (IS_ERR(hwacc_regulator[hwacc_dev])) {
- pr_err("prcmu: failed to get supply %s\n",
- hwacc_regulator_name[hwacc_dev]);
- r = PTR_ERR(hwacc_regulator[hwacc_dev]);
- goto out;
- }
- }
-
- if (ram_retention) {
- if (hwacc_ret_regulator[hwacc_dev] == NULL) {
- hwacc_ret_regulator[hwacc_dev] = regulator_get(NULL,
- hwacc_ret_regulator_name[hwacc_dev]);
- if (IS_ERR(hwacc_ret_regulator[hwacc_dev])) {
- pr_err("prcmu: failed to get supply %s\n",
- hwacc_ret_regulator_name[hwacc_dev]);
- r = PTR_ERR(hwacc_ret_regulator[hwacc_dev]);
- goto out;
- }
- }
- }
-
- /* set regulators */
- if (ram_retention) {
- if (enable_ret && !hwacc_ret_enabled[hwacc_dev]) {
- r = regulator_enable(hwacc_ret_regulator[hwacc_dev]);
- if (r < 0) {
- pr_err("prcmu_set_hwacc: ret enable failed\n");
- goto out;
- }
- hwacc_ret_enabled[hwacc_dev] = true;
- }
- }
-
- if (enable && !hwacc_enabled[hwacc_dev]) {
- r = regulator_enable(hwacc_regulator[hwacc_dev]);
- if (r < 0) {
- pr_err("prcmu_set_hwacc: enable failed\n");
- goto out;
- }
- hwacc_enabled[hwacc_dev] = true;
- }
-
- if (!enable && hwacc_enabled[hwacc_dev]) {
- r = regulator_disable(hwacc_regulator[hwacc_dev]);
- if (r < 0) {
- pr_err("prcmu_set_hwacc: disable failed\n");
- goto out;
- }
- hwacc_enabled[hwacc_dev] = false;
- }
-
- if (ram_retention) {
- if (!enable_ret && hwacc_ret_enabled[hwacc_dev]) {
- r = regulator_disable(hwacc_ret_regulator[hwacc_dev]);
- if (r < 0) {
- pr_err("prcmu_set_hwacc: ret disable failed\n");
- goto out;
- }
- hwacc_ret_enabled[hwacc_dev] = false;
- }
- }
-
-out:
- return r;
-}
-EXPORT_SYMBOL(prcmu_set_hwacc);
-
-/**
* db8500_prcmu_set_epod - set the state of a EPOD (power domain)
* @epod_id: The EPOD to set
* @epod_state: The new EPOD state
@@ -1375,7 +1434,7 @@ static int request_timclk(bool enable)
return 0;
}
-static int request_reg_clock(u8 clock, bool enable)
+static int request_clock(u8 clock, bool enable)
{
u32 val;
unsigned long flags;
@@ -1386,14 +1445,14 @@ static int request_reg_clock(u8 clock, bool enable)
while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
cpu_relax();
- val = readl(_PRCMU_BASE + clk_mgt[clock].offset);
+ val = readl(clk_mgt[clock].reg);
if (enable) {
val |= (PRCM_CLK_MGT_CLKEN | clk_mgt[clock].pllsw);
} else {
clk_mgt[clock].pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);
val &= ~(PRCM_CLK_MGT_CLKEN | PRCM_CLK_MGT_CLKPLLSW_MASK);
}
- writel(val, (_PRCMU_BASE + clk_mgt[clock].offset));
+ writel(val, clk_mgt[clock].reg);
/* Release the HW semaphore. */
writel(0, PRCM_SEM);
@@ -1413,7 +1472,7 @@ static int request_sga_clock(u8 clock, bool enable)
writel(val | PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS);
}
- ret = request_reg_clock(clock, enable);
+ ret = request_clock(clock, enable);
if (!ret && !enable) {
val = readl(PRCM_CGATING_BYPASS);
@@ -1423,6 +1482,78 @@ static int request_sga_clock(u8 clock, bool enable)
return ret;
}
+static inline bool plldsi_locked(void)
+{
+ return (readl(PRCM_PLLDSI_LOCKP) &
+ (PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 |
+ PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3)) ==
+ (PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 |
+ PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3);
+}
+
+static int request_plldsi(bool enable)
+{
+ int r = 0;
+ u32 val;
+
+ writel((PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP |
+ PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI), (enable ?
+ PRCM_MMIP_LS_CLAMP_CLR : PRCM_MMIP_LS_CLAMP_SET));
+
+ val = readl(PRCM_PLLDSI_ENABLE);
+ if (enable)
+ val |= PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE;
+ else
+ val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE;
+ writel(val, PRCM_PLLDSI_ENABLE);
+
+ if (enable) {
+ unsigned int i;
+ bool locked = plldsi_locked();
+
+ for (i = 10; !locked && (i > 0); --i) {
+ udelay(100);
+ locked = plldsi_locked();
+ }
+ if (locked) {
+ writel(PRCM_APE_RESETN_DSIPLL_RESETN,
+ PRCM_APE_RESETN_SET);
+ } else {
+ writel((PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP |
+ PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI),
+ PRCM_MMIP_LS_CLAMP_SET);
+ val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE;
+ writel(val, PRCM_PLLDSI_ENABLE);
+ r = -EAGAIN;
+ }
+ } else {
+ writel(PRCM_APE_RESETN_DSIPLL_RESETN, PRCM_APE_RESETN_CLR);
+ }
+ return r;
+}
+
+static int request_dsiclk(u8 n, bool enable)
+{
+ u32 val;
+
+ val = readl(PRCM_DSI_PLLOUT_SEL);
+ val &= ~dsiclk[n].divsel_mask;
+ val |= ((enable ? dsiclk[n].divsel : PRCM_DSI_PLLOUT_SEL_OFF) <<
+ dsiclk[n].divsel_shift);
+ writel(val, PRCM_DSI_PLLOUT_SEL);
+ return 0;
+}
+
+static int request_dsiescclk(u8 n, bool enable)
+{
+ u32 val;
+
+ val = readl(PRCM_DSITVCLK_DIV);
+ enable ? (val |= dsiescclk[n].en) : (val &= ~dsiescclk[n].en);
+ writel(val, PRCM_DSITVCLK_DIV);
+ return 0;
+}
+
/**
* db8500_prcmu_request_clock() - Request for a clock to be enabled or disabled.
* @clock: The clock for which the request is made.
@@ -1433,21 +1564,435 @@ static int request_sga_clock(u8 clock, bool enable)
*/
int db8500_prcmu_request_clock(u8 clock, bool enable)
{
- switch(clock) {
- case PRCMU_SGACLK:
+ if (clock == PRCMU_SGACLK)
return request_sga_clock(clock, enable);
- case PRCMU_TIMCLK:
+ else if (clock < PRCMU_NUM_REG_CLOCKS)
+ return request_clock(clock, enable);
+ else if (clock == PRCMU_TIMCLK)
return request_timclk(enable);
- case PRCMU_SYSCLK:
+ else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
+ return request_dsiclk((clock - PRCMU_DSI0CLK), enable);
+ else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK))
+ return request_dsiescclk((clock - PRCMU_DSI0ESCCLK), enable);
+ else if (clock == PRCMU_PLLDSI)
+ return request_plldsi(enable);
+ else if (clock == PRCMU_SYSCLK)
return request_sysclk(enable);
- case PRCMU_PLLSOC1:
+ else if ((clock == PRCMU_PLLSOC0) || (clock == PRCMU_PLLSOC1))
return request_pll(clock, enable);
+ else
+ return -EINVAL;
+}
+
+static unsigned long pll_rate(void __iomem *reg, unsigned long src_rate,
+ int branch)
+{
+ u64 rate;
+ u32 val;
+ u32 d;
+ u32 div = 1;
+
+ val = readl(reg);
+
+ rate = src_rate;
+ rate *= ((val & PRCM_PLL_FREQ_D_MASK) >> PRCM_PLL_FREQ_D_SHIFT);
+
+ d = ((val & PRCM_PLL_FREQ_N_MASK) >> PRCM_PLL_FREQ_N_SHIFT);
+ if (d > 1)
+ div *= d;
+
+ d = ((val & PRCM_PLL_FREQ_R_MASK) >> PRCM_PLL_FREQ_R_SHIFT);
+ if (d > 1)
+ div *= d;
+
+ if (val & PRCM_PLL_FREQ_SELDIV2)
+ div *= 2;
+
+ if ((branch == PLL_FIX) || ((branch == PLL_DIV) &&
+ (val & PRCM_PLL_FREQ_DIV2EN) &&
+ ((reg == PRCM_PLLSOC0_FREQ) ||
+ (reg == PRCM_PLLDDR_FREQ))))
+ div *= 2;
+
+ (void)do_div(rate, div);
+
+ return (unsigned long)rate;
+}
+
+#define ROOT_CLOCK_RATE 38400000
+
+static unsigned long clock_rate(u8 clock)
+{
+ u32 val;
+ u32 pllsw;
+ unsigned long rate = ROOT_CLOCK_RATE;
+
+ val = readl(clk_mgt[clock].reg);
+
+ if (val & PRCM_CLK_MGT_CLK38) {
+ if (clk_mgt[clock].clk38div && (val & PRCM_CLK_MGT_CLK38DIV))
+ rate /= 2;
+ return rate;
+ }
+
+ val |= clk_mgt[clock].pllsw;
+ pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);
+
+ if (pllsw == PRCM_CLK_MGT_CLKPLLSW_SOC0)
+ rate = pll_rate(PRCM_PLLSOC0_FREQ, rate, clk_mgt[clock].branch);
+ else if (pllsw == PRCM_CLK_MGT_CLKPLLSW_SOC1)
+ rate = pll_rate(PRCM_PLLSOC1_FREQ, rate, clk_mgt[clock].branch);
+ else if (pllsw == PRCM_CLK_MGT_CLKPLLSW_DDR)
+ rate = pll_rate(PRCM_PLLDDR_FREQ, rate, clk_mgt[clock].branch);
+ else
+ return 0;
+
+ if ((clock == PRCMU_SGACLK) &&
+ (val & PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN)) {
+ u64 r = (rate * 10);
+
+ (void)do_div(r, 25);
+ return (unsigned long)r;
+ }
+ val &= PRCM_CLK_MGT_CLKPLLDIV_MASK;
+ if (val)
+ return rate / val;
+ else
+ return 0;
+}
+
+static unsigned long dsiclk_rate(u8 n)
+{
+ u32 divsel;
+ u32 div = 1;
+
+ divsel = readl(PRCM_DSI_PLLOUT_SEL);
+ divsel = ((divsel & dsiclk[n].divsel_mask) >> dsiclk[n].divsel_shift);
+
+ if (divsel == PRCM_DSI_PLLOUT_SEL_OFF)
+ divsel = dsiclk[n].divsel;
+
+ switch (divsel) {
+ case PRCM_DSI_PLLOUT_SEL_PHI_4:
+ div *= 2;
+ case PRCM_DSI_PLLOUT_SEL_PHI_2:
+ div *= 2;
+ case PRCM_DSI_PLLOUT_SEL_PHI:
+ return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK),
+ PLL_RAW) / div;
default:
- break;
+ return 0;
+ }
+}
+
+static unsigned long dsiescclk_rate(u8 n)
+{
+ u32 div;
+
+ div = readl(PRCM_DSITVCLK_DIV);
+ div = ((div & dsiescclk[n].div_mask) >> (dsiescclk[n].div_shift));
+ return clock_rate(PRCMU_TVCLK) / max((u32)1, div);
+}
+
+unsigned long prcmu_clock_rate(u8 clock)
+{
+ if (clock < PRCMU_NUM_REG_CLOCKS)
+ return clock_rate(clock);
+ else if (clock == PRCMU_TIMCLK)
+ return ROOT_CLOCK_RATE / 16;
+ else if (clock == PRCMU_SYSCLK)
+ return ROOT_CLOCK_RATE;
+ else if (clock == PRCMU_PLLSOC0)
+ return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
+ else if (clock == PRCMU_PLLSOC1)
+ return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
+ else if (clock == PRCMU_PLLDDR)
+ return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
+ else if (clock == PRCMU_PLLDSI)
+ return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK),
+ PLL_RAW);
+ else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
+ return dsiclk_rate(clock - PRCMU_DSI0CLK);
+ else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK))
+ return dsiescclk_rate(clock - PRCMU_DSI0ESCCLK);
+ else
+ return 0;
+}
+
+static unsigned long clock_source_rate(u32 clk_mgt_val, int branch)
+{
+ if (clk_mgt_val & PRCM_CLK_MGT_CLK38)
+ return ROOT_CLOCK_RATE;
+ clk_mgt_val &= PRCM_CLK_MGT_CLKPLLSW_MASK;
+ if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_SOC0)
+ return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, branch);
+ else if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_SOC1)
+ return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, branch);
+ else if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_DDR)
+ return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, branch);
+ else
+ return 0;
+}
+
+static u32 clock_divider(unsigned long src_rate, unsigned long rate)
+{
+ u32 div;
+
+ div = (src_rate / rate);
+ if (div == 0)
+ return 1;
+ if (rate < (src_rate / div))
+ div++;
+ return div;
+}
+
+static long round_clock_rate(u8 clock, unsigned long rate)
+{
+ u32 val;
+ u32 div;
+ unsigned long src_rate;
+ long rounded_rate;
+
+ val = readl(clk_mgt[clock].reg);
+ src_rate = clock_source_rate((val | clk_mgt[clock].pllsw),
+ clk_mgt[clock].branch);
+ div = clock_divider(src_rate, rate);
+ if (val & PRCM_CLK_MGT_CLK38) {
+ if (clk_mgt[clock].clk38div) {
+ if (div > 2)
+ div = 2;
+ } else {
+ div = 1;
+ }
+ } else if ((clock == PRCMU_SGACLK) && (div == 3)) {
+ u64 r = (src_rate * 10);
+
+ (void)do_div(r, 25);
+ if (r <= rate)
+ return (unsigned long)r;
+ }
+ rounded_rate = (src_rate / min(div, (u32)31));
+
+ return rounded_rate;
+}
+
+#define MIN_PLL_VCO_RATE 600000000ULL
+#define MAX_PLL_VCO_RATE 1680640000ULL
+
+static long round_plldsi_rate(unsigned long rate)
+{
+ long rounded_rate = 0;
+ unsigned long src_rate;
+ unsigned long rem;
+ u32 r;
+
+ src_rate = clock_rate(PRCMU_HDMICLK);
+ rem = rate;
+
+ for (r = 7; (rem > 0) && (r > 0); r--) {
+ u64 d;
+
+ d = (r * rate);
+ (void)do_div(d, src_rate);
+ if (d < 6)
+ d = 6;
+ else if (d > 255)
+ d = 255;
+ d *= src_rate;
+ if (((2 * d) < (r * MIN_PLL_VCO_RATE)) ||
+ ((r * MAX_PLL_VCO_RATE) < (2 * d)))
+ continue;
+ (void)do_div(d, r);
+ if (rate < d) {
+ if (rounded_rate == 0)
+ rounded_rate = (long)d;
+ break;
+ }
+ if ((rate - d) < rem) {
+ rem = (rate - d);
+ rounded_rate = (long)d;
+ }
+ }
+ return rounded_rate;
+}
+
+static long round_dsiclk_rate(unsigned long rate)
+{
+ u32 div;
+ unsigned long src_rate;
+ long rounded_rate;
+
+ src_rate = pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK),
+ PLL_RAW);
+ div = clock_divider(src_rate, rate);
+ rounded_rate = (src_rate / ((div > 2) ? 4 : div));
+
+ return rounded_rate;
+}
+
+static long round_dsiescclk_rate(unsigned long rate)
+{
+ u32 div;
+ unsigned long src_rate;
+ long rounded_rate;
+
+ src_rate = clock_rate(PRCMU_TVCLK);
+ div = clock_divider(src_rate, rate);
+ rounded_rate = (src_rate / min(div, (u32)255));
+
+ return rounded_rate;
+}
+
+long prcmu_round_clock_rate(u8 clock, unsigned long rate)
+{
+ if (clock < PRCMU_NUM_REG_CLOCKS)
+ return round_clock_rate(clock, rate);
+ else if (clock == PRCMU_PLLDSI)
+ return round_plldsi_rate(rate);
+ else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
+ return round_dsiclk_rate(rate);
+ else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK))
+ return round_dsiescclk_rate(rate);
+ else
+ return (long)prcmu_clock_rate(clock);
+}
+
+static void set_clock_rate(u8 clock, unsigned long rate)
+{
+ u32 val;
+ u32 div;
+ unsigned long src_rate;
+ unsigned long flags;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ val = readl(clk_mgt[clock].reg);
+ src_rate = clock_source_rate((val | clk_mgt[clock].pllsw),
+ clk_mgt[clock].branch);
+ div = clock_divider(src_rate, rate);
+ if (val & PRCM_CLK_MGT_CLK38) {
+ if (clk_mgt[clock].clk38div) {
+ if (div > 1)
+ val |= PRCM_CLK_MGT_CLK38DIV;
+ else
+ val &= ~PRCM_CLK_MGT_CLK38DIV;
+ }
+ } else if (clock == PRCMU_SGACLK) {
+ val &= ~(PRCM_CLK_MGT_CLKPLLDIV_MASK |
+ PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN);
+ if (div == 3) {
+ u64 r = (src_rate * 10);
+
+ (void)do_div(r, 25);
+ if (r <= rate) {
+ val |= PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN;
+ div = 0;
+ }
+ }
+ val |= min(div, (u32)31);
+ } else {
+ val &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK;
+ val |= min(div, (u32)31);
+ }
+ writel(val, clk_mgt[clock].reg);
+
+ /* Release the HW semaphore. */
+ writel(0, PRCM_SEM);
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+}
+
+static int set_plldsi_rate(unsigned long rate)
+{
+ unsigned long src_rate;
+ unsigned long rem;
+ u32 pll_freq = 0;
+ u32 r;
+
+ src_rate = clock_rate(PRCMU_HDMICLK);
+ rem = rate;
+
+ for (r = 7; (rem > 0) && (r > 0); r--) {
+ u64 d;
+ u64 hwrate;
+
+ d = (r * rate);
+ (void)do_div(d, src_rate);
+ if (d < 6)
+ d = 6;
+ else if (d > 255)
+ d = 255;
+ hwrate = (d * src_rate);
+ if (((2 * hwrate) < (r * MIN_PLL_VCO_RATE)) ||
+ ((r * MAX_PLL_VCO_RATE) < (2 * hwrate)))
+ continue;
+ (void)do_div(hwrate, r);
+ if (rate < hwrate) {
+ if (pll_freq == 0)
+ pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) |
+ (r << PRCM_PLL_FREQ_R_SHIFT));
+ break;
+ }
+ if ((rate - hwrate) < rem) {
+ rem = (rate - hwrate);
+ pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) |
+ (r << PRCM_PLL_FREQ_R_SHIFT));
+ }
}
+ if (pll_freq == 0)
+ return -EINVAL;
+
+ pll_freq |= (1 << PRCM_PLL_FREQ_N_SHIFT);
+ writel(pll_freq, PRCM_PLLDSI_FREQ);
+
+ return 0;
+}
+
+static void set_dsiclk_rate(u8 n, unsigned long rate)
+{
+ u32 val;
+ u32 div;
+
+ div = clock_divider(pll_rate(PRCM_PLLDSI_FREQ,
+ clock_rate(PRCMU_HDMICLK), PLL_RAW), rate);
+
+ dsiclk[n].divsel = (div == 1) ? PRCM_DSI_PLLOUT_SEL_PHI :
+ (div == 2) ? PRCM_DSI_PLLOUT_SEL_PHI_2 :
+ /* else */ PRCM_DSI_PLLOUT_SEL_PHI_4;
+
+ val = readl(PRCM_DSI_PLLOUT_SEL);
+ val &= ~dsiclk[n].divsel_mask;
+ val |= (dsiclk[n].divsel << dsiclk[n].divsel_shift);
+ writel(val, PRCM_DSI_PLLOUT_SEL);
+}
+
+static void set_dsiescclk_rate(u8 n, unsigned long rate)
+{
+ u32 val;
+ u32 div;
+
+ div = clock_divider(clock_rate(PRCMU_TVCLK), rate);
+ val = readl(PRCM_DSITVCLK_DIV);
+ val &= ~dsiescclk[n].div_mask;
+ val |= (min(div, (u32)255) << dsiescclk[n].div_shift);
+ writel(val, PRCM_DSITVCLK_DIV);
+}
+
+int prcmu_set_clock_rate(u8 clock, unsigned long rate)
+{
if (clock < PRCMU_NUM_REG_CLOCKS)
- return request_reg_clock(clock, enable);
- return -EINVAL;
+ set_clock_rate(clock, rate);
+ else if (clock == PRCMU_PLLDSI)
+ return set_plldsi_rate(rate);
+ else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
+ set_dsiclk_rate((clock - PRCMU_DSI0CLK), rate);
+ else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK))
+ set_dsiescclk_rate((clock - PRCMU_DSI0ESCCLK), rate);
+ return 0;
}
int db8500_prcmu_config_esram0_deep_sleep(u8 state)
@@ -1476,7 +2021,7 @@ int db8500_prcmu_config_esram0_deep_sleep(u8 state)
return 0;
}
-int prcmu_config_hotdog(u8 threshold)
+int db8500_prcmu_config_hotdog(u8 threshold)
{
mutex_lock(&mb4_transfer.lock);
@@ -1494,7 +2039,7 @@ int prcmu_config_hotdog(u8 threshold)
return 0;
}
-int prcmu_config_hotmon(u8 low, u8 high)
+int db8500_prcmu_config_hotmon(u8 low, u8 high)
{
mutex_lock(&mb4_transfer.lock);
@@ -1533,7 +2078,7 @@ static int config_hot_period(u16 val)
return 0;
}
-int prcmu_start_temp_sense(u16 cycles32k)
+int db8500_prcmu_start_temp_sense(u16 cycles32k)
{
if (cycles32k == 0xFFFF)
return -EINVAL;
@@ -1541,7 +2086,7 @@ int prcmu_start_temp_sense(u16 cycles32k)
return config_hot_period(cycles32k);
}
-int prcmu_stop_temp_sense(void)
+int db8500_prcmu_stop_temp_sense(void)
{
return config_hot_period(0xFFFF);
}
@@ -1570,7 +2115,7 @@ static int prcmu_a9wdog(u8 cmd, u8 d0, u8 d1, u8 d2, u8 d3)
}
-int prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
+int db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
{
BUG_ON(num == 0 || num > 0xf);
return prcmu_a9wdog(MB4H_A9WDOG_CONF, num, 0, 0,
@@ -1578,17 +2123,17 @@ int prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
A9WDOG_AUTO_OFF_DIS);
}
-int prcmu_enable_a9wdog(u8 id)
+int db8500_prcmu_enable_a9wdog(u8 id)
{
return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0);
}
-int prcmu_disable_a9wdog(u8 id)
+int db8500_prcmu_disable_a9wdog(u8 id)
{
return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0);
}
-int prcmu_kick_a9wdog(u8 id)
+int db8500_prcmu_kick_a9wdog(u8 id)
{
return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0);
}
@@ -1596,16 +2141,8 @@ int prcmu_kick_a9wdog(u8 id)
/*
* timeout is 28 bit, in ms.
*/
-#define MAX_WATCHDOG_TIMEOUT 131000
-int prcmu_load_a9wdog(u8 id, u32 timeout)
+int db8500_prcmu_load_a9wdog(u8 id, u32 timeout)
{
- if (timeout > MAX_WATCHDOG_TIMEOUT)
- /*
- * Due to calculation bug in prcmu fw, timeouts
- * can't be bigger than 131 seconds.
- */
- return -EINVAL;
-
return prcmu_a9wdog(MB4H_A9WDOG_LOAD,
(id & A9WDOG_ID_MASK) |
/*
@@ -1619,41 +2156,6 @@ int prcmu_load_a9wdog(u8 id, u32 timeout)
}
/**
- * prcmu_set_clock_divider() - Configure the clock divider.
- * @clock: The clock for which the request is made.
- * @divider: The clock divider. (< 32)
- *
- * This function should only be used by the clock implementation.
- * Do not use it from any other place!
- */
-int prcmu_set_clock_divider(u8 clock, u8 divider)
-{
- u32 val;
- unsigned long flags;
-
- if ((clock >= PRCMU_NUM_REG_CLOCKS) || (divider < 1) || (31 < divider))
- return -EINVAL;
-
- spin_lock_irqsave(&clk_mgt_lock, flags);
-
- /* Grab the HW semaphore. */
- while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
- cpu_relax();
-
- val = readl(_PRCMU_BASE + clk_mgt[clock].offset);
- val &= ~(PRCM_CLK_MGT_CLKPLLDIV_MASK);
- val |= (u32)divider;
- writel(val, (_PRCMU_BASE + clk_mgt[clock].offset));
-
- /* Release the HW semaphore. */
- writel(0, PRCM_SEM);
-
- spin_unlock_irqrestore(&clk_mgt_lock, flags);
-
- return 0;
-}
-
-/**
* prcmu_abb_read() - Read register value(s) from the ABB.
* @slave: The I2C slave address.
* @reg: The (start) register address.
@@ -1675,6 +2177,7 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
cpu_relax();
+ writeb(0, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB5));
writeb(PRCMU_I2C_READ(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));
writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS));
writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG));
@@ -1700,16 +2203,19 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
}
/**
- * prcmu_abb_write() - Write register value(s) to the ABB.
+ * prcmu_abb_write_masked() - Write masked register value(s) to the ABB.
* @slave: The I2C slave address.
* @reg: The (start) register address.
* @value: The value(s) to write.
+ * @mask: The mask(s) to use.
* @size: The number of registers to write.
*
- * Reads register value(s) from the ABB.
+ * Writes masked register value(s) to the ABB.
+ * For each @value, only the bits set to 1 in the corresponding @mask
+ * will be written. The other bits are not changed.
* @size has to be 1 for the current firmware version.
*/
-int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
+int prcmu_abb_write_masked(u8 slave, u8 reg, u8 *value, u8 *mask, u8 size)
{
int r;
@@ -1721,6 +2227,7 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
cpu_relax();
+ writeb(~*mask, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB5));
writeb(PRCMU_I2C_WRITE(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));
writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS));
writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG));
@@ -1743,6 +2250,23 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
}
/**
+ * prcmu_abb_write() - Write register value(s) to the ABB.
+ * @slave: The I2C slave address.
+ * @reg: The (start) register address.
+ * @value: The value(s) to write.
+ * @size: The number of registers to write.
+ *
+ * Writes register value(s) to the ABB.
+ * @size has to be 1 for the current firmware version.
+ */
+int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
+{
+ u8 mask = ~0;
+
+ return prcmu_abb_write_masked(slave, reg, value, &mask, size);
+}
+
+/**
* prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem
*/
void prcmu_ac_wake_req(void)
@@ -1850,9 +2374,9 @@ u16 db8500_prcmu_get_reset_code(void)
}
/**
- * prcmu_reset_modem - ask the PRCMU to reset modem
+ * db8500_prcmu_reset_modem - ask the PRCMU to reset modem
*/
-void prcmu_modem_reset(void)
+void db8500_prcmu_modem_reset(void)
{
mutex_lock(&mb1_transfer.lock);
@@ -2099,6 +2623,26 @@ static struct irq_chip prcmu_irq_chip = {
.irq_unmask = prcmu_irq_unmask,
};
+static char *fw_project_name(u8 project)
+{
+ switch (project) {
+ case PRCMU_FW_PROJECT_U8500:
+ return "U8500";
+ case PRCMU_FW_PROJECT_U8500_C2:
+ return "U8500 C2";
+ case PRCMU_FW_PROJECT_U9500:
+ return "U9500";
+ case PRCMU_FW_PROJECT_U9500_C2:
+ return "U9500 C2";
+ case PRCMU_FW_PROJECT_U8520:
+ return "U8520";
+ case PRCMU_FW_PROJECT_U8420:
+ return "U8420";
+ default:
+ return "Unknown";
+ }
+}
+
void __init db8500_prcmu_early_init(void)
{
unsigned int i;
@@ -2108,11 +2652,13 @@ void __init db8500_prcmu_early_init(void)
if (tcpm_base != NULL) {
u32 version;
version = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET);
- prcmu_version.project_number = version & 0xFF;
- prcmu_version.api_version = (version >> 8) & 0xFF;
- prcmu_version.func_version = (version >> 16) & 0xFF;
- prcmu_version.errata = (version >> 24) & 0xFF;
- pr_info("PRCMU firmware version %d.%d.%d\n",
+ fw_info.version.project = version & 0xFF;
+ fw_info.version.api_version = (version >> 8) & 0xFF;
+ fw_info.version.func_version = (version >> 16) & 0xFF;
+ fw_info.version.errata = (version >> 24) & 0xFF;
+ fw_info.valid = true;
+ pr_info("PRCMU firmware: %s, version %d.%d.%d\n",
+ fw_project_name(fw_info.version.project),
(version >> 8) & 0xFF, (version >> 16) & 0xFF,
(version >> 24) & 0xFF);
iounmap(tcpm_base);
@@ -2130,6 +2676,7 @@ void __init db8500_prcmu_early_init(void)
init_completion(&mb0_transfer.ac_wake_work);
mutex_init(&mb1_transfer.lock);
init_completion(&mb1_transfer.work);
+ mb1_transfer.ape_opp = APE_NO_CHANGE;
mutex_init(&mb2_transfer.lock);
init_completion(&mb2_transfer.work);
spin_lock_init(&mb2_transfer.auto_pm_lock);
@@ -2154,7 +2701,7 @@ void __init db8500_prcmu_early_init(void)
}
}
-static void __init db8500_prcmu_init_clkforce(void)
+static void __init init_prcm_registers(void)
{
u32 val;
@@ -2186,19 +2733,17 @@ static struct regulator_consumer_supply db8500_vape_consumers[] = {
REGULATOR_SUPPLY("vcore", "uart1"),
REGULATOR_SUPPLY("vcore", "uart2"),
REGULATOR_SUPPLY("v-ape", "nmk-ske-keypad.0"),
+ REGULATOR_SUPPLY("v-hsi", "ste_hsi.0"),
};
static struct regulator_consumer_supply db8500_vsmps2_consumers[] = {
- /* CG2900 and CW1200 power to off-chip peripherals */
- REGULATOR_SUPPLY("gbf_1v8", "cg2900-uart.0"),
- REGULATOR_SUPPLY("wlan_1v8", "cw1200.0"),
REGULATOR_SUPPLY("musb_1v8", "ab8500-usb.0"),
/* AV8100 regulator */
REGULATOR_SUPPLY("hdmi_1v8", "0-0070"),
};
static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = {
- REGULATOR_SUPPLY("vsupply", "b2r2.0"),
+ REGULATOR_SUPPLY("vsupply", "b2r2_bus"),
REGULATOR_SUPPLY("vsupply", "mcde"),
};
@@ -2235,6 +2780,7 @@ static struct regulator_consumer_supply db8500_esram12_consumers[] = {
static struct regulator_consumer_supply db8500_esram34_consumers[] = {
REGULATOR_SUPPLY("v-esram34", "mcde"),
REGULATOR_SUPPLY("esram34", "cm_control"),
+ REGULATOR_SUPPLY("lcla_esram", "dma40.0"),
};
static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
@@ -2291,7 +2837,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
},
},
[DB8500_REGULATOR_SWITCH_SVAMMDSP] = {
- .supply_regulator = "db8500-vape",
+ /* dependency to u8500-vape is handled outside regulator framework */
.constraints = {
.name = "db8500-sva-mmdsp",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
@@ -2307,7 +2853,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
},
},
[DB8500_REGULATOR_SWITCH_SVAPIPE] = {
- .supply_regulator = "db8500-vape",
+ /* dependency to u8500-vape is handled outside regulator framework */
.constraints = {
.name = "db8500-sva-pipe",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
@@ -2316,7 +2862,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.num_consumer_supplies = ARRAY_SIZE(db8500_svapipe_consumers),
},
[DB8500_REGULATOR_SWITCH_SIAMMDSP] = {
- .supply_regulator = "db8500-vape",
+ /* dependency to u8500-vape is handled outside regulator framework */
.constraints = {
.name = "db8500-sia-mmdsp",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
@@ -2331,7 +2877,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
},
},
[DB8500_REGULATOR_SWITCH_SIAPIPE] = {
- .supply_regulator = "db8500-vape",
+ /* dependency to u8500-vape is handled outside regulator framework */
.constraints = {
.name = "db8500-sia-pipe",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
@@ -2359,7 +2905,10 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
.num_consumer_supplies = ARRAY_SIZE(db8500_b2r2_mcde_consumers),
},
[DB8500_REGULATOR_SWITCH_ESRAM12] = {
- .supply_regulator = "db8500-vape",
+ /*
+ * esram12 is set in retention and supplied by Vsafe when Vape is off,
+ * no need to hold Vape
+ */
.constraints = {
.name = "db8500-esram12",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
@@ -2374,7 +2923,10 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
},
},
[DB8500_REGULATOR_SWITCH_ESRAM34] = {
- .supply_regulator = "db8500-vape",
+ /*
+ * esram34 is set in retention and supplied by Vsafe when Vape is off,
+ * no need to hold Vape
+ */
.constraints = {
.name = "db8500-esram34",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
@@ -2412,7 +2964,7 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)
if (ux500_is_svp())
return -ENODEV;
- db8500_prcmu_init_clkforce();
+ init_prcm_registers();
/* Clean up the mailbox interrupts after pre-kernel code. */
writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h
index ec22e9f..3a0bf91 100644
--- a/drivers/mfd/dbx500-prcmu-regs.h
+++ b/drivers/mfd/dbx500-prcmu-regs.h
@@ -17,41 +17,41 @@
#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
-#define PRCM_SVACLK_MGT_OFF 0x008
-#define PRCM_SIACLK_MGT_OFF 0x00C
-#define PRCM_SGACLK_MGT_OFF 0x014
-#define PRCM_UARTCLK_MGT_OFF 0x018
-#define PRCM_MSP02CLK_MGT_OFF 0x01C
-#define PRCM_I2CCLK_MGT_OFF 0x020
-#define PRCM_SDMMCCLK_MGT_OFF 0x024
-#define PRCM_SLIMCLK_MGT_OFF 0x028
-#define PRCM_PER1CLK_MGT_OFF 0x02C
-#define PRCM_PER2CLK_MGT_OFF 0x030
-#define PRCM_PER3CLK_MGT_OFF 0x034
-#define PRCM_PER5CLK_MGT_OFF 0x038
-#define PRCM_PER6CLK_MGT_OFF 0x03C
-#define PRCM_PER7CLK_MGT_OFF 0x040
-#define PRCM_PWMCLK_MGT_OFF 0x044 /* for DB5500 */
-#define PRCM_IRDACLK_MGT_OFF 0x048 /* for DB5500 */
-#define PRCM_IRRCCLK_MGT_OFF 0x04C /* for DB5500 */
-#define PRCM_LCDCLK_MGT_OFF 0x044
-#define PRCM_BMLCLK_MGT_OFF 0x04C
-#define PRCM_HSITXCLK_MGT_OFF 0x050
-#define PRCM_HSIRXCLK_MGT_OFF 0x054
-#define PRCM_HDMICLK_MGT_OFF 0x058
-#define PRCM_APEATCLK_MGT_OFF 0x05C
-#define PRCM_APETRACECLK_MGT_OFF 0x060
-#define PRCM_MCDECLK_MGT_OFF 0x064
-#define PRCM_IPI2CCLK_MGT_OFF 0x068
-#define PRCM_DSIALTCLK_MGT_OFF 0x06C
-#define PRCM_DMACLK_MGT_OFF 0x074
-#define PRCM_B2R2CLK_MGT_OFF 0x078
-#define PRCM_TVCLK_MGT_OFF 0x07C
-#define PRCM_UNIPROCLK_MGT_OFF 0x278
-#define PRCM_SSPCLK_MGT_OFF 0x280
-#define PRCM_RNGCLK_MGT_OFF 0x284
-#define PRCM_UICCCLK_MGT_OFF 0x27C
-#define PRCM_MSP1CLK_MGT_OFF 0x288
+#define PRCM_CLK_MGT(_offset) (void __iomem *)(IO_ADDRESS(U8500_PRCMU_BASE) \
+ + _offset)
+#define PRCM_ACLK_MGT PRCM_CLK_MGT(0x004)
+#define PRCM_SVACLK_MGT PRCM_CLK_MGT(0x008)
+#define PRCM_SIACLK_MGT PRCM_CLK_MGT(0x00C)
+#define PRCM_SGACLK_MGT PRCM_CLK_MGT(0x014)
+#define PRCM_UARTCLK_MGT PRCM_CLK_MGT(0x018)
+#define PRCM_MSP02CLK_MGT PRCM_CLK_MGT(0x01C)
+#define PRCM_I2CCLK_MGT PRCM_CLK_MGT(0x020)
+#define PRCM_SDMMCCLK_MGT PRCM_CLK_MGT(0x024)
+#define PRCM_SLIMCLK_MGT PRCM_CLK_MGT(0x028)
+#define PRCM_PER1CLK_MGT PRCM_CLK_MGT(0x02C)
+#define PRCM_PER2CLK_MGT PRCM_CLK_MGT(0x030)
+#define PRCM_PER3CLK_MGT PRCM_CLK_MGT(0x034)
+#define PRCM_PER5CLK_MGT PRCM_CLK_MGT(0x038)
+#define PRCM_PER6CLK_MGT PRCM_CLK_MGT(0x03C)
+#define PRCM_PER7CLK_MGT PRCM_CLK_MGT(0x040)
+#define PRCM_LCDCLK_MGT PRCM_CLK_MGT(0x044)
+#define PRCM_BMLCLK_MGT PRCM_CLK_MGT(0x04C)
+#define PRCM_HSITXCLK_MGT PRCM_CLK_MGT(0x050)
+#define PRCM_HSIRXCLK_MGT PRCM_CLK_MGT(0x054)
+#define PRCM_HDMICLK_MGT PRCM_CLK_MGT(0x058)
+#define PRCM_APEATCLK_MGT PRCM_CLK_MGT(0x05C)
+#define PRCM_APETRACECLK_MGT PRCM_CLK_MGT(0x060)
+#define PRCM_MCDECLK_MGT PRCM_CLK_MGT(0x064)
+#define PRCM_IPI2CCLK_MGT PRCM_CLK_MGT(0x068)
+#define PRCM_DSIALTCLK_MGT PRCM_CLK_MGT(0x06C)
+#define PRCM_DMACLK_MGT PRCM_CLK_MGT(0x074)
+#define PRCM_B2R2CLK_MGT PRCM_CLK_MGT(0x078)
+#define PRCM_TVCLK_MGT PRCM_CLK_MGT(0x07C)
+#define PRCM_UNIPROCLK_MGT PRCM_CLK_MGT(0x278)
+#define PRCM_SSPCLK_MGT PRCM_CLK_MGT(0x280)
+#define PRCM_RNGCLK_MGT PRCM_CLK_MGT(0x284)
+#define PRCM_UICCCLK_MGT PRCM_CLK_MGT(0x27C)
+#define PRCM_MSP1CLK_MGT PRCM_CLK_MGT(0x288)
#define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118)
#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f
@@ -79,6 +79,8 @@
/* ARM WFI Standby signal register */
#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130)
+#define PRCM_ARM_WFI_STANDBY_WFI0 0x08
+#define PRCM_ARM_WFI_STANDBY_WFI1 0x10
#define PRCM_IOCR (_PRCMU_BASE + 0x310)
#define PRCM_IOCR_IOFORCE 0x1
@@ -131,20 +133,58 @@
#define PRCM_MMIP_LS_CLAMP_SET (_PRCMU_BASE + 0x420)
#define PRCM_MMIP_LS_CLAMP_CLR (_PRCMU_BASE + 0x424)
+#define PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP BIT(11)
+#define PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI BIT(22)
+
/* PRCMU clock/PLL/reset registers */
+#define PRCM_PLLSOC0_FREQ (_PRCMU_BASE + 0x080)
+#define PRCM_PLLSOC1_FREQ (_PRCMU_BASE + 0x084)
+#define PRCM_PLLDDR_FREQ (_PRCMU_BASE + 0x08C)
+#define PRCM_PLL_FREQ_D_SHIFT 0
+#define PRCM_PLL_FREQ_D_MASK BITS(0, 7)
+#define PRCM_PLL_FREQ_N_SHIFT 8
+#define PRCM_PLL_FREQ_N_MASK BITS(8, 13)
+#define PRCM_PLL_FREQ_R_SHIFT 16
+#define PRCM_PLL_FREQ_R_MASK BITS(16, 18)
+#define PRCM_PLL_FREQ_SELDIV2 BIT(24)
+#define PRCM_PLL_FREQ_DIV2EN BIT(25)
+
#define PRCM_PLLDSI_FREQ (_PRCMU_BASE + 0x500)
#define PRCM_PLLDSI_ENABLE (_PRCMU_BASE + 0x504)
#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508)
-#define PRCM_LCDCLK_MGT (_PRCMU_BASE + PRCM_LCDCLK_MGT_OFF)
-#define PRCM_MCDECLK_MGT (_PRCMU_BASE + PRCM_MCDECLK_MGT_OFF)
-#define PRCM_HDMICLK_MGT (_PRCMU_BASE + PRCM_HDMICLK_MGT_OFF)
-#define PRCM_TVCLK_MGT (_PRCMU_BASE + PRCM_TVCLK_MGT_OFF)
#define PRCM_DSI_PLLOUT_SEL (_PRCMU_BASE + 0x530)
#define PRCM_DSITVCLK_DIV (_PRCMU_BASE + 0x52C)
#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508)
#define PRCM_APE_RESETN_SET (_PRCMU_BASE + 0x1E4)
#define PRCM_APE_RESETN_CLR (_PRCMU_BASE + 0x1E8)
+#define PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE BIT(0)
+
+#define PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 BIT(0)
+#define PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3 BIT(1)
+
+#define PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_SHIFT 0
+#define PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_MASK BITS(0, 2)
+#define PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_SHIFT 8
+#define PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_MASK BITS(8, 10)
+
+#define PRCM_DSI_PLLOUT_SEL_OFF 0
+#define PRCM_DSI_PLLOUT_SEL_PHI 1
+#define PRCM_DSI_PLLOUT_SEL_PHI_2 2
+#define PRCM_DSI_PLLOUT_SEL_PHI_4 3
+
+#define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_SHIFT 0
+#define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_MASK BITS(0, 7)
+#define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_SHIFT 8
+#define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_MASK BITS(8, 15)
+#define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_SHIFT 16
+#define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_MASK BITS(16, 23)
+#define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_EN BIT(24)
+#define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_EN BIT(25)
+#define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_EN BIT(26)
+
+#define PRCM_APE_RESETN_DSIPLL_RESETN BIT(14)
+
#define PRCM_CLKOCR (_PRCMU_BASE + 0x1CC)
#define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0)
#define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13)
@@ -183,9 +223,15 @@
#define PRCM_CLKOCR_CLKOSEL1_MASK BITS(22, 24)
#define PRCM_CLKOCR_CLK1TYPE BIT(28)
-#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
-#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
-#define PRCM_CLK_MGT_CLKEN BIT(8)
+#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
+#define PRCM_CLK_MGT_CLKPLLSW_SOC0 BIT(5)
+#define PRCM_CLK_MGT_CLKPLLSW_SOC1 BIT(6)
+#define PRCM_CLK_MGT_CLKPLLSW_DDR BIT(7)
+#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
+#define PRCM_CLK_MGT_CLKEN BIT(8)
+#define PRCM_CLK_MGT_CLK38 BIT(9)
+#define PRCM_CLK_MGT_CLK38DIV BIT(11)
+#define PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN BIT(12)
/* GPIOCR register */
#define PRCM_GPIOCR_SPI2_SELECT BIT(23)
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index 7122386..9fd4f63 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -560,6 +560,8 @@ EXPORT_SYMBOL(mc13xxx_get_flags);
#define MC13XXX_ADC1_CHAN0_SHIFT 5
#define MC13XXX_ADC1_CHAN1_SHIFT 8
+#define MC13783_ADC1_ATO_SHIFT 11
+#define MC13783_ADC1_ATOX (1 << 19)
struct mc13xxx_adcdone_data {
struct mc13xxx *mc13xxx;
@@ -580,7 +582,8 @@ static irqreturn_t mc13xxx_handler_adcdone(int irq, void *data)
#define MC13XXX_ADC_WORKING (1 << 0)
int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,
- unsigned int channel, unsigned int *sample)
+ unsigned int channel, u8 ato, bool atox,
+ unsigned int *sample)
{
u32 adc0, adc1, old_adc0;
int i, ret;
@@ -631,6 +634,9 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,
return -EINVAL;
}
+ adc1 |= ato << MC13783_ADC1_ATO_SHIFT;
+ if (atox)
+ adc1 |= MC13783_ADC1_ATOX;
dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__);
mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE,
mc13xxx_handler_adcdone, __func__, &adcdone_data);
@@ -813,7 +819,8 @@ err_revision:
mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN)
- mc13xxx_add_subdevice(mc13xxx, "%s-ts");
+ mc13xxx_add_subdevice_pdata(mc13xxx, "%s-ts",
+ &pdata->touch, sizeof(pdata->touch));
if (pdata) {
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c
index 86cc3f7..62e5e36 100644
--- a/drivers/mfd/mcp-core.c
+++ b/drivers/mfd/mcp-core.c
@@ -19,9 +19,6 @@
#include <linux/string.h>
#include <linux/mfd/mcp.h>
-#include <mach/dma.h>
-#include <asm/system.h>
-
#define to_mcp(d) container_of(d, struct mcp, attached_device)
#define to_mcp_driver(d) container_of(d, struct mcp_driver, drv)
@@ -48,39 +45,11 @@ static int mcp_bus_remove(struct device *dev)
return 0;
}
-static int mcp_bus_suspend(struct device *dev, pm_message_t state)
-{
- struct mcp *mcp = to_mcp(dev);
- int ret = 0;
-
- if (dev->driver) {
- struct mcp_driver *drv = to_mcp_driver(dev->driver);
-
- ret = drv->suspend(mcp, state);
- }
- return ret;
-}
-
-static int mcp_bus_resume(struct device *dev)
-{
- struct mcp *mcp = to_mcp(dev);
- int ret = 0;
-
- if (dev->driver) {
- struct mcp_driver *drv = to_mcp_driver(dev->driver);
-
- ret = drv->resume(mcp);
- }
- return ret;
-}
-
static struct bus_type mcp_bus_type = {
.name = "mcp",
.match = mcp_bus_match,
.probe = mcp_bus_probe,
.remove = mcp_bus_remove,
- .suspend = mcp_bus_suspend,
- .resume = mcp_bus_resume,
};
/**
@@ -208,6 +177,7 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size)
mcp = kzalloc(sizeof(struct mcp) + size, GFP_KERNEL);
if (mcp) {
spin_lock_init(&mcp->lock);
+ device_initialize(&mcp->attached_device);
mcp->attached_device.parent = parent;
mcp->attached_device.bus = &mcp_bus_type;
mcp->attached_device.dma_mask = parent->dma_mask;
@@ -217,18 +187,25 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size)
}
EXPORT_SYMBOL(mcp_host_alloc);
-int mcp_host_register(struct mcp *mcp)
+int mcp_host_add(struct mcp *mcp, void *pdata)
{
+ mcp->attached_device.platform_data = pdata;
dev_set_name(&mcp->attached_device, "mcp0");
- return device_register(&mcp->attached_device);
+ return device_add(&mcp->attached_device);
+}
+EXPORT_SYMBOL(mcp_host_add);
+
+void mcp_host_del(struct mcp *mcp)
+{
+ device_del(&mcp->attached_device);
}
-EXPORT_SYMBOL(mcp_host_register);
+EXPORT_SYMBOL(mcp_host_del);
-void mcp_host_unregister(struct mcp *mcp)
+void mcp_host_free(struct mcp *mcp)
{
- device_unregister(&mcp->attached_device);
+ put_device(&mcp->attached_device);
}
-EXPORT_SYMBOL(mcp_host_unregister);
+EXPORT_SYMBOL(mcp_host_free);
int mcp_driver_register(struct mcp_driver *mcpdrv)
{
diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c
index 02c53a0..c54e244 100644
--- a/drivers/mfd/mcp-sa11x0.c
+++ b/drivers/mfd/mcp-sa11x0.c
@@ -13,51 +13,60 @@
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/mfd/mcp.h>
-#include <mach/dma.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
-#include <asm/system.h>
#include <mach/mcp.h>
-#include <mach/assabet.h>
-
+#define DRIVER_NAME "sa11x0-mcp"
struct mcp_sa11x0 {
- u32 mccr0;
- u32 mccr1;
+ void __iomem *base0;
+ void __iomem *base1;
+ u32 mccr0;
+ u32 mccr1;
};
+/* Register offsets */
+#define MCCR0(m) ((m)->base0 + 0x00)
+#define MCDR0(m) ((m)->base0 + 0x08)
+#define MCDR1(m) ((m)->base0 + 0x0c)
+#define MCDR2(m) ((m)->base0 + 0x10)
+#define MCSR(m) ((m)->base0 + 0x18)
+#define MCCR1(m) ((m)->base1 + 0x00)
+
#define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp))
static void
mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)
{
- unsigned int mccr0;
+ struct mcp_sa11x0 *m = priv(mcp);
divisor /= 32;
- mccr0 = Ser4MCCR0 & ~0x00007f00;
- mccr0 |= divisor << 8;
- Ser4MCCR0 = mccr0;
+ m->mccr0 &= ~0x00007f00;
+ m->mccr0 |= divisor << 8;
+ writel_relaxed(m->mccr0, MCCR0(m));
}
static void
mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
{
- unsigned int mccr0;
+ struct mcp_sa11x0 *m = priv(mcp);
divisor /= 32;
- mccr0 = Ser4MCCR0 & ~0x0000007f;
- mccr0 |= divisor;
- Ser4MCCR0 = mccr0;
+ m->mccr0 &= ~0x0000007f;
+ m->mccr0 |= divisor;
+ writel_relaxed(m->mccr0, MCCR0(m));
}
/*
@@ -69,14 +78,15 @@ mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
static void
mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
{
+ struct mcp_sa11x0 *m = priv(mcp);
int ret = -ETIME;
int i;
- Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff);
+ writel_relaxed(reg << 17 | MCDR2_Wr | (val & 0xffff), MCDR2(m));
for (i = 0; i < 2; i++) {
udelay(mcp->rw_timeout);
- if (Ser4MCSR & MCSR_CWC) {
+ if (readl_relaxed(MCSR(m)) & MCSR_CWC) {
ret = 0;
break;
}
@@ -95,15 +105,16 @@ mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
static unsigned int
mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
{
+ struct mcp_sa11x0 *m = priv(mcp);
int ret = -ETIME;
int i;
- Ser4MCDR2 = reg << 17 | MCDR2_Rd;
+ writel_relaxed(reg << 17 | MCDR2_Rd, MCDR2(m));
for (i = 0; i < 2; i++) {
udelay(mcp->rw_timeout);
- if (Ser4MCSR & MCSR_CRC) {
- ret = Ser4MCDR2 & 0xffff;
+ if (readl_relaxed(MCSR(m)) & MCSR_CRC) {
+ ret = readl_relaxed(MCDR2(m)) & 0xffff;
break;
}
}
@@ -116,13 +127,19 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
static void mcp_sa11x0_enable(struct mcp *mcp)
{
- Ser4MCSR = -1;
- Ser4MCCR0 |= MCCR0_MCE;
+ struct mcp_sa11x0 *m = priv(mcp);
+
+ writel(-1, MCSR(m));
+ m->mccr0 |= MCCR0_MCE;
+ writel_relaxed(m->mccr0, MCCR0(m));
}
static void mcp_sa11x0_disable(struct mcp *mcp)
{
- Ser4MCCR0 &= ~MCCR0_MCE;
+ struct mcp_sa11x0 *m = priv(mcp);
+
+ m->mccr0 &= ~MCCR0_MCE;
+ writel_relaxed(m->mccr0, MCCR0(m));
}
/*
@@ -137,55 +154,64 @@ static struct mcp_ops mcp_sa11x0 = {
.disable = mcp_sa11x0_disable,
};
-static int mcp_sa11x0_probe(struct platform_device *pdev)
+static int mcp_sa11x0_probe(struct platform_device *dev)
{
- struct mcp_plat_data *data = pdev->dev.platform_data;
+ struct mcp_plat_data *data = dev->dev.platform_data;
+ struct resource *mem0, *mem1;
+ struct mcp_sa11x0 *m;
struct mcp *mcp;
int ret;
if (!data)
return -ENODEV;
- if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp"))
- return -EBUSY;
+ mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
+ if (!mem0 || !mem1)
+ return -ENXIO;
+
+ if (!request_mem_region(mem0->start, resource_size(mem0),
+ DRIVER_NAME)) {
+ ret = -EBUSY;
+ goto err_mem0;
+ }
- mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0));
+ if (!request_mem_region(mem1->start, resource_size(mem1),
+ DRIVER_NAME)) {
+ ret = -EBUSY;
+ goto err_mem1;
+ }
+
+ mcp = mcp_host_alloc(&dev->dev, sizeof(struct mcp_sa11x0));
if (!mcp) {
ret = -ENOMEM;
- goto release;
+ goto err_alloc;
}
mcp->owner = THIS_MODULE;
mcp->ops = &mcp_sa11x0;
mcp->sclk_rate = data->sclk_rate;
- mcp->dma_audio_rd = DMA_Ser4MCP0Rd;
- mcp->dma_audio_wr = DMA_Ser4MCP0Wr;
- mcp->dma_telco_rd = DMA_Ser4MCP1Rd;
- mcp->dma_telco_wr = DMA_Ser4MCP1Wr;
- mcp->gpio_base = data->gpio_base;
- platform_set_drvdata(pdev, mcp);
+ m = priv(mcp);
+ m->mccr0 = data->mccr0 | 0x7f7f;
+ m->mccr1 = data->mccr1;
- if (machine_is_assabet()) {
- ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
+ m->base0 = ioremap(mem0->start, resource_size(mem0));
+ m->base1 = ioremap(mem1->start, resource_size(mem1));
+ if (!m->base0 || !m->base1) {
+ ret = -ENOMEM;
+ goto err_ioremap;
}
- /*
- * Setup the PPC unit correctly.
- */
- PPDR &= ~PPC_RXD4;
- PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
- PSDR |= PPC_RXD4;
- PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
- PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+ platform_set_drvdata(dev, mcp);
/*
* Initialise device. Note that we initially
* set the sampling rate to minimum.
*/
- Ser4MCSR = -1;
- Ser4MCCR1 = data->mccr1;
- Ser4MCCR0 = data->mccr0 | 0x7f7f;
+ writel_relaxed(-1, MCSR(m));
+ writel_relaxed(m->mccr1, MCCR1(m));
+ writel_relaxed(m->mccr0, MCCR0(m));
/*
* Calculate the read/write timeout (us) from the bit clock
@@ -195,62 +221,90 @@ static int mcp_sa11x0_probe(struct platform_device *pdev)
mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) /
mcp->sclk_rate;
- ret = mcp_host_register(mcp);
+ ret = mcp_host_add(mcp, data->codec_pdata);
if (ret == 0)
- goto out;
+ return 0;
- release:
- release_mem_region(0x80060000, 0x60);
- platform_set_drvdata(pdev, NULL);
+ platform_set_drvdata(dev, NULL);
- out:
+ err_ioremap:
+ iounmap(m->base1);
+ iounmap(m->base0);
+ mcp_host_free(mcp);
+ err_alloc:
+ release_mem_region(mem1->start, resource_size(mem1));
+ err_mem1:
+ release_mem_region(mem0->start, resource_size(mem0));
+ err_mem0:
return ret;
}
static int mcp_sa11x0_remove(struct platform_device *dev)
{
struct mcp *mcp = platform_get_drvdata(dev);
+ struct mcp_sa11x0 *m = priv(mcp);
+ struct resource *mem0, *mem1;
+
+ if (m->mccr0 & MCCR0_MCE)
+ dev_warn(&dev->dev,
+ "device left active (missing disable call?)\n");
+
+ mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
platform_set_drvdata(dev, NULL);
- mcp_host_unregister(mcp);
- release_mem_region(0x80060000, 0x60);
+ mcp_host_del(mcp);
+ iounmap(m->base1);
+ iounmap(m->base0);
+ mcp_host_free(mcp);
+ release_mem_region(mem1->start, resource_size(mem1));
+ release_mem_region(mem0->start, resource_size(mem0));
return 0;
}
-static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int mcp_sa11x0_suspend(struct device *dev)
{
- struct mcp *mcp = platform_get_drvdata(dev);
+ struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev));
+
+ if (m->mccr0 & MCCR0_MCE)
+ dev_warn(dev, "device left active (missing disable call?)\n");
- priv(mcp)->mccr0 = Ser4MCCR0;
- priv(mcp)->mccr1 = Ser4MCCR1;
- Ser4MCCR0 &= ~MCCR0_MCE;
+ writel(m->mccr0 & ~MCCR0_MCE, MCCR0(m));
return 0;
}
-static int mcp_sa11x0_resume(struct platform_device *dev)
+static int mcp_sa11x0_resume(struct device *dev)
{
- struct mcp *mcp = platform_get_drvdata(dev);
+ struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev));
- Ser4MCCR1 = priv(mcp)->mccr1;
- Ser4MCCR0 = priv(mcp)->mccr0;
+ writel_relaxed(m->mccr1, MCCR1(m));
+ writel_relaxed(m->mccr0, MCCR0(m));
return 0;
}
-
-/*
- * The driver for the SA11x0 MCP port.
- */
-MODULE_ALIAS("platform:sa11x0-mcp");
+#endif
+
+static const struct dev_pm_ops mcp_sa11x0_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = mcp_sa11x0_suspend,
+ .freeze = mcp_sa11x0_suspend,
+ .poweroff = mcp_sa11x0_suspend,
+ .resume_noirq = mcp_sa11x0_resume,
+ .thaw_noirq = mcp_sa11x0_resume,
+ .restore_noirq = mcp_sa11x0_resume,
+#endif
+};
static struct platform_driver mcp_sa11x0_driver = {
.probe = mcp_sa11x0_probe,
.remove = mcp_sa11x0_remove,
- .suspend = mcp_sa11x0_suspend,
- .resume = mcp_sa11x0_resume,
.driver = {
- .name = "sa11x0-mcp",
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &mcp_sa11x0_pm_ops,
},
};
@@ -259,6 +313,7 @@ static struct platform_driver mcp_sa11x0_driver = {
*/
module_platform_driver(mcp_sa11x0_driver);
+MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 411f523..ffc3d48 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -162,7 +162,7 @@ int mfd_add_devices(struct device *parent, int id,
atomic_t *cnts;
/* initialize reference counting for all cells */
- cnts = kcalloc(sizeof(*cnts), n_devs, GFP_KERNEL);
+ cnts = kcalloc(n_devs, sizeof(*cnts), GFP_KERNEL);
if (!cnts)
return -ENOMEM;
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 68ac2c5..95a2e54 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -170,7 +170,7 @@ struct usbhs_hcd_omap {
/*-------------------------------------------------------------------------*/
const char usbhs_driver_name[] = USBHS_DRIVER_NAME;
-static u64 usbhs_dmamask = ~(u32)0;
+static u64 usbhs_dmamask = DMA_BIT_MASK(32);
/*-------------------------------------------------------------------------*/
@@ -223,7 +223,7 @@ static struct platform_device *omap_usbhs_alloc_child(const char *name,
}
child->dev.dma_mask = &usbhs_dmamask;
- child->dev.coherent_dma_mask = 0xffffffff;
+ dma_set_coherent_mask(&child->dev, DMA_BIT_MASK(32));
child->dev.parent = dev;
ret = platform_device_add(child);
@@ -799,14 +799,13 @@ static int __devinit usbhs_omap_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, omap);
+ omap_usbhs_init(dev);
ret = omap_usbhs_alloc_children(pdev);
if (ret) {
dev_err(dev, "omap_usbhs_alloc_children failed\n");
goto err_alloc;
}
- omap_usbhs_init(dev);
-
goto end_probe;
err_alloc:
diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
index ff1a7e7..189c2f0 100644
--- a/drivers/mfd/pcf50633-core.c
+++ b/drivers/mfd/pcf50633-core.c
@@ -46,13 +46,7 @@ EXPORT_SYMBOL_GPL(pcf50633_read_block);
int pcf50633_write_block(struct pcf50633 *pcf , u8 reg,
int nr_regs, u8 *data)
{
- int ret;
-
- ret = regmap_raw_write(pcf->regmap, reg, data, nr_regs);
- if (ret != 0)
- return ret;
-
- return nr_regs;
+ return regmap_raw_write(pcf->regmap, reg, data, nr_regs);
}
EXPORT_SYMBOL_GPL(pcf50633_write_block);
diff --git a/drivers/mfd/pcf50633-gpio.c b/drivers/mfd/pcf50633-gpio.c
index 9ab19a8..d02ddf2 100644
--- a/drivers/mfd/pcf50633-gpio.c
+++ b/drivers/mfd/pcf50633-gpio.c
@@ -19,32 +19,7 @@
#include <linux/mfd/pcf50633/core.h>
#include <linux/mfd/pcf50633/gpio.h>
-
-enum pcf50633_regulator_id {
- PCF50633_REGULATOR_AUTO,
- PCF50633_REGULATOR_DOWN1,
- PCF50633_REGULATOR_DOWN2,
- PCF50633_REGULATOR_LDO1,
- PCF50633_REGULATOR_LDO2,
- PCF50633_REGULATOR_LDO3,
- PCF50633_REGULATOR_LDO4,
- PCF50633_REGULATOR_LDO5,
- PCF50633_REGULATOR_LDO6,
- PCF50633_REGULATOR_HCLDO,
- PCF50633_REGULATOR_MEMLDO,
-};
-
-#define PCF50633_REG_AUTOOUT 0x1a
-#define PCF50633_REG_DOWN1OUT 0x1e
-#define PCF50633_REG_DOWN2OUT 0x22
-#define PCF50633_REG_MEMLDOOUT 0x26
-#define PCF50633_REG_LDO1OUT 0x2d
-#define PCF50633_REG_LDO2OUT 0x2f
-#define PCF50633_REG_LDO3OUT 0x31
-#define PCF50633_REG_LDO4OUT 0x33
-#define PCF50633_REG_LDO5OUT 0x35
-#define PCF50633_REG_LDO6OUT 0x37
-#define PCF50633_REG_HCLDOOUT 0x39
+#include <linux/mfd/pcf50633/pmic.h>
static const u8 pcf50633_regulator_registers[PCF50633_NUM_REGULATORS] = {
[PCF50633_REGULATOR_AUTO] = PCF50633_REG_AUTOOUT,
diff --git a/drivers/mfd/pcf50633-irq.c b/drivers/mfd/pcf50633-irq.c
index 048a3b9..498286c 100644
--- a/drivers/mfd/pcf50633-irq.c
+++ b/drivers/mfd/pcf50633-irq.c
@@ -19,12 +19,7 @@
#include <linux/slab.h>
#include <linux/mfd/pcf50633/core.h>
-
-/* Two MBCS registers used during cold start */
-#define PCF50633_REG_MBCS1 0x4b
-#define PCF50633_REG_MBCS2 0x4c
-#define PCF50633_MBCS1_USBPRES 0x01
-#define PCF50633_MBCS1_ADAPTPRES 0x01
+#include <linux/mfd/pcf50633/mbc.h>
int pcf50633_register_irq(struct pcf50633 *pcf, int irq,
void (*handler) (int, void *), void *data)
diff --git a/drivers/mfd/rc5t583-irq.c b/drivers/mfd/rc5t583-irq.c
new file mode 100644
index 0000000..fa6f80f
--- /dev/null
+++ b/drivers/mfd/rc5t583-irq.c
@@ -0,0 +1,408 @@
+/*
+ * Interrupt driver for RICOH583 power management chip.
+ *
+ * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
+ * Author: Laxman dewangan <ldewangan@nvidia.com>
+ *
+ * based on code
+ * Copyright (C) 2011 RICOH COMPANY,LTD
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/mfd/rc5t583.h>
+
+enum int_type {
+ SYS_INT = 0x1,
+ DCDC_INT = 0x2,
+ RTC_INT = 0x4,
+ ADC_INT = 0x8,
+ GPIO_INT = 0x10,
+};
+
+static int gpedge_add[] = {
+ RC5T583_GPIO_GPEDGE2,
+ RC5T583_GPIO_GPEDGE2
+};
+
+static int irq_en_add[] = {
+ RC5T583_INT_EN_SYS1,
+ RC5T583_INT_EN_SYS2,
+ RC5T583_INT_EN_DCDC,
+ RC5T583_INT_EN_RTC,
+ RC5T583_INT_EN_ADC1,
+ RC5T583_INT_EN_ADC2,
+ RC5T583_INT_EN_ADC3,
+ RC5T583_GPIO_EN_INT
+};
+
+static int irq_mon_add[] = {
+ RC5T583_INT_MON_SYS1,
+ RC5T583_INT_MON_SYS2,
+ RC5T583_INT_MON_DCDC,
+ RC5T583_INT_MON_RTC,
+ RC5T583_INT_IR_ADCL,
+ RC5T583_INT_IR_ADCH,
+ RC5T583_INT_IR_ADCEND,
+ RC5T583_INT_IR_GPIOF,
+ RC5T583_INT_IR_GPIOR
+};
+
+static int irq_clr_add[] = {
+ RC5T583_INT_IR_SYS1,
+ RC5T583_INT_IR_SYS2,
+ RC5T583_INT_IR_DCDC,
+ RC5T583_INT_IR_RTC,
+ RC5T583_INT_IR_ADCL,
+ RC5T583_INT_IR_ADCH,
+ RC5T583_INT_IR_ADCEND,
+ RC5T583_INT_IR_GPIOF,
+ RC5T583_INT_IR_GPIOR
+};
+
+static int main_int_type[] = {
+ SYS_INT,
+ SYS_INT,
+ DCDC_INT,
+ RTC_INT,
+ ADC_INT,
+ ADC_INT,
+ ADC_INT,
+ GPIO_INT,
+ GPIO_INT,
+};
+
+struct rc5t583_irq_data {
+ u8 int_type;
+ u8 master_bit;
+ u8 int_en_bit;
+ u8 mask_reg_index;
+ int grp_index;
+};
+
+#define RC5T583_IRQ(_int_type, _master_bit, _grp_index, \
+ _int_bit, _mask_ind) \
+ { \
+ .int_type = _int_type, \
+ .master_bit = _master_bit, \
+ .grp_index = _grp_index, \
+ .int_en_bit = _int_bit, \
+ .mask_reg_index = _mask_ind, \
+ }
+
+static const struct rc5t583_irq_data rc5t583_irqs[RC5T583_MAX_IRQS] = {
+ [RC5T583_IRQ_ONKEY] = RC5T583_IRQ(SYS_INT, 0, 0, 0, 0),
+ [RC5T583_IRQ_ACOK] = RC5T583_IRQ(SYS_INT, 0, 1, 1, 0),
+ [RC5T583_IRQ_LIDOPEN] = RC5T583_IRQ(SYS_INT, 0, 2, 2, 0),
+ [RC5T583_IRQ_PREOT] = RC5T583_IRQ(SYS_INT, 0, 3, 3, 0),
+ [RC5T583_IRQ_CLKSTP] = RC5T583_IRQ(SYS_INT, 0, 4, 4, 0),
+ [RC5T583_IRQ_ONKEY_OFF] = RC5T583_IRQ(SYS_INT, 0, 5, 5, 0),
+ [RC5T583_IRQ_WD] = RC5T583_IRQ(SYS_INT, 0, 7, 7, 0),
+ [RC5T583_IRQ_EN_PWRREQ1] = RC5T583_IRQ(SYS_INT, 0, 8, 0, 1),
+ [RC5T583_IRQ_EN_PWRREQ2] = RC5T583_IRQ(SYS_INT, 0, 9, 1, 1),
+ [RC5T583_IRQ_PRE_VINDET] = RC5T583_IRQ(SYS_INT, 0, 10, 2, 1),
+
+ [RC5T583_IRQ_DC0LIM] = RC5T583_IRQ(DCDC_INT, 1, 0, 0, 2),
+ [RC5T583_IRQ_DC1LIM] = RC5T583_IRQ(DCDC_INT, 1, 1, 1, 2),
+ [RC5T583_IRQ_DC2LIM] = RC5T583_IRQ(DCDC_INT, 1, 2, 2, 2),
+ [RC5T583_IRQ_DC3LIM] = RC5T583_IRQ(DCDC_INT, 1, 3, 3, 2),
+
+ [RC5T583_IRQ_CTC] = RC5T583_IRQ(RTC_INT, 2, 0, 0, 3),
+ [RC5T583_IRQ_YALE] = RC5T583_IRQ(RTC_INT, 2, 5, 5, 3),
+ [RC5T583_IRQ_DALE] = RC5T583_IRQ(RTC_INT, 2, 6, 6, 3),
+ [RC5T583_IRQ_WALE] = RC5T583_IRQ(RTC_INT, 2, 7, 7, 3),
+
+ [RC5T583_IRQ_AIN1L] = RC5T583_IRQ(ADC_INT, 3, 0, 0, 4),
+ [RC5T583_IRQ_AIN2L] = RC5T583_IRQ(ADC_INT, 3, 1, 1, 4),
+ [RC5T583_IRQ_AIN3L] = RC5T583_IRQ(ADC_INT, 3, 2, 2, 4),
+ [RC5T583_IRQ_VBATL] = RC5T583_IRQ(ADC_INT, 3, 3, 3, 4),
+ [RC5T583_IRQ_VIN3L] = RC5T583_IRQ(ADC_INT, 3, 4, 4, 4),
+ [RC5T583_IRQ_VIN8L] = RC5T583_IRQ(ADC_INT, 3, 5, 5, 4),
+ [RC5T583_IRQ_AIN1H] = RC5T583_IRQ(ADC_INT, 3, 6, 0, 5),
+ [RC5T583_IRQ_AIN2H] = RC5T583_IRQ(ADC_INT, 3, 7, 1, 5),
+ [RC5T583_IRQ_AIN3H] = RC5T583_IRQ(ADC_INT, 3, 8, 2, 5),
+ [RC5T583_IRQ_VBATH] = RC5T583_IRQ(ADC_INT, 3, 9, 3, 5),
+ [RC5T583_IRQ_VIN3H] = RC5T583_IRQ(ADC_INT, 3, 10, 4, 5),
+ [RC5T583_IRQ_VIN8H] = RC5T583_IRQ(ADC_INT, 3, 11, 5, 5),
+ [RC5T583_IRQ_ADCEND] = RC5T583_IRQ(ADC_INT, 3, 12, 0, 6),
+
+ [RC5T583_IRQ_GPIO0] = RC5T583_IRQ(GPIO_INT, 4, 0, 0, 7),
+ [RC5T583_IRQ_GPIO1] = RC5T583_IRQ(GPIO_INT, 4, 1, 1, 7),
+ [RC5T583_IRQ_GPIO2] = RC5T583_IRQ(GPIO_INT, 4, 2, 2, 7),
+ [RC5T583_IRQ_GPIO3] = RC5T583_IRQ(GPIO_INT, 4, 3, 3, 7),
+ [RC5T583_IRQ_GPIO4] = RC5T583_IRQ(GPIO_INT, 4, 4, 4, 7),
+ [RC5T583_IRQ_GPIO5] = RC5T583_IRQ(GPIO_INT, 4, 5, 5, 7),
+ [RC5T583_IRQ_GPIO6] = RC5T583_IRQ(GPIO_INT, 4, 6, 6, 7),
+ [RC5T583_IRQ_GPIO7] = RC5T583_IRQ(GPIO_INT, 4, 7, 7, 7),
+};
+
+static void rc5t583_irq_lock(struct irq_data *irq_data)
+{
+ struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
+ mutex_lock(&rc5t583->irq_lock);
+}
+
+static void rc5t583_irq_unmask(struct irq_data *irq_data)
+{
+ struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
+ unsigned int __irq = irq_data->irq - rc5t583->irq_base;
+ const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq];
+
+ rc5t583->group_irq_en[data->grp_index] |= 1 << data->grp_index;
+ rc5t583->intc_inten_reg |= 1 << data->master_bit;
+ rc5t583->irq_en_reg[data->mask_reg_index] |= 1 << data->int_en_bit;
+}
+
+static void rc5t583_irq_mask(struct irq_data *irq_data)
+{
+ struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
+ unsigned int __irq = irq_data->irq - rc5t583->irq_base;
+ const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq];
+
+ rc5t583->group_irq_en[data->grp_index] &= ~(1 << data->grp_index);
+ if (!rc5t583->group_irq_en[data->grp_index])
+ rc5t583->intc_inten_reg &= ~(1 << data->master_bit);
+
+ rc5t583->irq_en_reg[data->mask_reg_index] &= ~(1 << data->int_en_bit);
+}
+
+static int rc5t583_irq_set_type(struct irq_data *irq_data, unsigned int type)
+{
+ struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
+ unsigned int __irq = irq_data->irq - rc5t583->irq_base;
+ const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq];
+ int val = 0;
+ int gpedge_index;
+ int gpedge_bit_pos;
+
+ /* Supporting only trigger level inetrrupt */
+ if ((data->int_type & GPIO_INT) && (type & IRQ_TYPE_EDGE_BOTH)) {
+ gpedge_index = data->int_en_bit / 4;
+ gpedge_bit_pos = data->int_en_bit % 4;
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ val |= 0x2;
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ val |= 0x1;
+
+ rc5t583->gpedge_reg[gpedge_index] &= ~(3 << gpedge_bit_pos);
+ rc5t583->gpedge_reg[gpedge_index] |= (val << gpedge_bit_pos);
+ rc5t583_irq_unmask(irq_data);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static void rc5t583_irq_sync_unlock(struct irq_data *irq_data)
+{
+ struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
+ int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(rc5t583->gpedge_reg); i++) {
+ ret = rc5t583_write(rc5t583->dev, gpedge_add[i],
+ rc5t583->gpedge_reg[i]);
+ if (ret < 0)
+ dev_warn(rc5t583->dev,
+ "Error in writing reg 0x%02x error: %d\n",
+ gpedge_add[i], ret);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rc5t583->irq_en_reg); i++) {
+ ret = rc5t583_write(rc5t583->dev, irq_en_add[i],
+ rc5t583->irq_en_reg[i]);
+ if (ret < 0)
+ dev_warn(rc5t583->dev,
+ "Error in writing reg 0x%02x error: %d\n",
+ irq_en_add[i], ret);
+ }
+
+ ret = rc5t583_write(rc5t583->dev, RC5T583_INTC_INTEN,
+ rc5t583->intc_inten_reg);
+ if (ret < 0)
+ dev_warn(rc5t583->dev,
+ "Error in writing reg 0x%02x error: %d\n",
+ RC5T583_INTC_INTEN, ret);
+
+ mutex_unlock(&rc5t583->irq_lock);
+}
+#ifdef CONFIG_PM_SLEEP
+static int rc5t583_irq_set_wake(struct irq_data *irq_data, unsigned int on)
+{
+ struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
+ return irq_set_irq_wake(rc5t583->chip_irq, on);
+}
+#else
+#define rc5t583_irq_set_wake NULL
+#endif
+
+static irqreturn_t rc5t583_irq(int irq, void *data)
+{
+ struct rc5t583 *rc5t583 = data;
+ uint8_t int_sts[RC5T583_MAX_INTERRUPT_MASK_REGS];
+ uint8_t master_int;
+ int i;
+ int ret;
+ unsigned int rtc_int_sts = 0;
+
+ /* Clear the status */
+ for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++)
+ int_sts[i] = 0;
+
+ ret = rc5t583_read(rc5t583->dev, RC5T583_INTC_INTMON, &master_int);
+ if (ret < 0) {
+ dev_err(rc5t583->dev,
+ "Error in reading reg 0x%02x error: %d\n",
+ RC5T583_INTC_INTMON, ret);
+ return IRQ_HANDLED;
+ }
+
+ for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; ++i) {
+ if (!(master_int & main_int_type[i]))
+ continue;
+
+ ret = rc5t583_read(rc5t583->dev, irq_mon_add[i], &int_sts[i]);
+ if (ret < 0) {
+ dev_warn(rc5t583->dev,
+ "Error in reading reg 0x%02x error: %d\n",
+ irq_mon_add[i], ret);
+ int_sts[i] = 0;
+ continue;
+ }
+
+ if (main_int_type[i] & RTC_INT) {
+ rtc_int_sts = 0;
+ if (int_sts[i] & 0x1)
+ rtc_int_sts |= BIT(6);
+ if (int_sts[i] & 0x2)
+ rtc_int_sts |= BIT(7);
+ if (int_sts[i] & 0x4)
+ rtc_int_sts |= BIT(0);
+ if (int_sts[i] & 0x8)
+ rtc_int_sts |= BIT(5);
+ }
+
+ ret = rc5t583_write(rc5t583->dev, irq_clr_add[i],
+ ~int_sts[i]);
+ if (ret < 0)
+ dev_warn(rc5t583->dev,
+ "Error in reading reg 0x%02x error: %d\n",
+ irq_clr_add[i], ret);
+
+ if (main_int_type[i] & RTC_INT)
+ int_sts[i] = rtc_int_sts;
+ }
+
+ /* Merge gpio interrupts for rising and falling case*/
+ int_sts[7] |= int_sts[8];
+
+ /* Call interrupt handler if enabled */
+ for (i = 0; i < RC5T583_MAX_IRQS; ++i) {
+ const struct rc5t583_irq_data *data = &rc5t583_irqs[i];
+ if ((int_sts[data->mask_reg_index] & (1 << data->int_en_bit)) &&
+ (rc5t583->group_irq_en[data->master_bit] &
+ (1 << data->grp_index)))
+ handle_nested_irq(rc5t583->irq_base + i);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct irq_chip rc5t583_irq_chip = {
+ .name = "rc5t583-irq",
+ .irq_mask = rc5t583_irq_mask,
+ .irq_unmask = rc5t583_irq_unmask,
+ .irq_bus_lock = rc5t583_irq_lock,
+ .irq_bus_sync_unlock = rc5t583_irq_sync_unlock,
+ .irq_set_type = rc5t583_irq_set_type,
+ .irq_set_wake = rc5t583_irq_set_wake,
+};
+
+int rc5t583_irq_init(struct rc5t583 *rc5t583, int irq, int irq_base)
+{
+ int i, ret;
+
+ if (!irq_base) {
+ dev_warn(rc5t583->dev, "No interrupt support on IRQ base\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&rc5t583->irq_lock);
+
+ /* Initailize all int register to 0 */
+ for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++) {
+ ret = rc5t583_write(rc5t583->dev, irq_en_add[i],
+ rc5t583->irq_en_reg[i]);
+ if (ret < 0)
+ dev_warn(rc5t583->dev,
+ "Error in writing reg 0x%02x error: %d\n",
+ irq_en_add[i], ret);
+ }
+
+ for (i = 0; i < RC5T583_MAX_GPEDGE_REG; i++) {
+ ret = rc5t583_write(rc5t583->dev, gpedge_add[i],
+ rc5t583->gpedge_reg[i]);
+ if (ret < 0)
+ dev_warn(rc5t583->dev,
+ "Error in writing reg 0x%02x error: %d\n",
+ gpedge_add[i], ret);
+ }
+
+ ret = rc5t583_write(rc5t583->dev, RC5T583_INTC_INTEN, 0x0);
+ if (ret < 0)
+ dev_warn(rc5t583->dev,
+ "Error in writing reg 0x%02x error: %d\n",
+ RC5T583_INTC_INTEN, ret);
+
+ /* Clear all interrupts in case they woke up active. */
+ for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++) {
+ ret = rc5t583_write(rc5t583->dev, irq_clr_add[i], 0);
+ if (ret < 0)
+ dev_warn(rc5t583->dev,
+ "Error in writing reg 0x%02x error: %d\n",
+ irq_clr_add[i], ret);
+ }
+
+ rc5t583->irq_base = irq_base;
+ rc5t583->chip_irq = irq;
+
+ for (i = 0; i < RC5T583_MAX_IRQS; i++) {
+ int __irq = i + rc5t583->irq_base;
+ irq_set_chip_data(__irq, rc5t583);
+ irq_set_chip_and_handler(__irq, &rc5t583_irq_chip,
+ handle_simple_irq);
+ irq_set_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(__irq, IRQF_VALID);
+#endif
+ }
+
+ ret = request_threaded_irq(irq, NULL, rc5t583_irq, IRQF_ONESHOT,
+ "rc5t583", rc5t583);
+ if (ret < 0)
+ dev_err(rc5t583->dev,
+ "Error in registering interrupt error: %d\n", ret);
+ return ret;
+}
+
+int rc5t583_irq_exit(struct rc5t583 *rc5t583)
+{
+ if (rc5t583->chip_irq)
+ free_irq(rc5t583->chip_irq, rc5t583);
+ return 0;
+}
diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c
new file mode 100644
index 0000000..99ef944
--- /dev/null
+++ b/drivers/mfd/rc5t583.c
@@ -0,0 +1,386 @@
+/*
+ * Core driver access RC5T583 power management chip.
+ *
+ * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
+ * Author: Laxman dewangan <ldewangan@nvidia.com>
+ *
+ * Based on code
+ * Copyright (C) 2011 RICOH COMPANY,LTD
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/rc5t583.h>
+#include <linux/regmap.h>
+
+#define RICOH_ONOFFSEL_REG 0x10
+#define RICOH_SWCTL_REG 0x5E
+
+struct deepsleep_control_data {
+ u8 reg_add;
+ u8 ds_pos_bit;
+};
+
+#define DEEPSLEEP_INIT(_id, _reg, _pos) \
+ { \
+ .reg_add = RC5T583_##_reg, \
+ .ds_pos_bit = _pos, \
+ }
+
+static struct deepsleep_control_data deepsleep_data[] = {
+ DEEPSLEEP_INIT(DC0, SLPSEQ1, 0),
+ DEEPSLEEP_INIT(DC1, SLPSEQ1, 4),
+ DEEPSLEEP_INIT(DC2, SLPSEQ2, 0),
+ DEEPSLEEP_INIT(DC3, SLPSEQ2, 4),
+ DEEPSLEEP_INIT(LDO0, SLPSEQ3, 0),
+ DEEPSLEEP_INIT(LDO1, SLPSEQ3, 4),
+ DEEPSLEEP_INIT(LDO2, SLPSEQ4, 0),
+ DEEPSLEEP_INIT(LDO3, SLPSEQ4, 4),
+ DEEPSLEEP_INIT(LDO4, SLPSEQ5, 0),
+ DEEPSLEEP_INIT(LDO5, SLPSEQ5, 4),
+ DEEPSLEEP_INIT(LDO6, SLPSEQ6, 0),
+ DEEPSLEEP_INIT(LDO7, SLPSEQ6, 4),
+ DEEPSLEEP_INIT(LDO8, SLPSEQ7, 0),
+ DEEPSLEEP_INIT(LDO9, SLPSEQ7, 4),
+ DEEPSLEEP_INIT(PSO0, SLPSEQ8, 0),
+ DEEPSLEEP_INIT(PSO1, SLPSEQ8, 4),
+ DEEPSLEEP_INIT(PSO2, SLPSEQ9, 0),
+ DEEPSLEEP_INIT(PSO3, SLPSEQ9, 4),
+ DEEPSLEEP_INIT(PSO4, SLPSEQ10, 0),
+ DEEPSLEEP_INIT(PSO5, SLPSEQ10, 4),
+ DEEPSLEEP_INIT(PSO6, SLPSEQ11, 0),
+ DEEPSLEEP_INIT(PSO7, SLPSEQ11, 4),
+};
+
+#define EXT_PWR_REQ \
+ (RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL)
+
+static struct mfd_cell rc5t583_subdevs[] = {
+ {.name = "rc5t583-regulator",},
+ {.name = "rc5t583-rtc", },
+ {.name = "rc5t583-key", }
+};
+
+int rc5t583_write(struct device *dev, uint8_t reg, uint8_t val)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev);
+ return regmap_write(rc5t583->regmap, reg, val);
+}
+
+int rc5t583_read(struct device *dev, uint8_t reg, uint8_t *val)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev);
+ unsigned int ival;
+ int ret;
+ ret = regmap_read(rc5t583->regmap, reg, &ival);
+ if (!ret)
+ *val = (uint8_t)ival;
+ return ret;
+}
+
+int rc5t583_set_bits(struct device *dev, unsigned int reg,
+ unsigned int bit_mask)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev);
+ return regmap_update_bits(rc5t583->regmap, reg, bit_mask, bit_mask);
+}
+
+int rc5t583_clear_bits(struct device *dev, unsigned int reg,
+ unsigned int bit_mask)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev);
+ return regmap_update_bits(rc5t583->regmap, reg, bit_mask, 0);
+}
+
+int rc5t583_update(struct device *dev, unsigned int reg,
+ unsigned int val, unsigned int mask)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev);
+ return regmap_update_bits(rc5t583->regmap, reg, mask, val);
+}
+
+static int __rc5t583_set_ext_pwrreq1_control(struct device *dev,
+ int id, int ext_pwr, int slots)
+{
+ int ret;
+ uint8_t sleepseq_val;
+ unsigned int en_bit;
+ unsigned int slot_bit;
+
+ if (id == RC5T583_DS_DC0) {
+ dev_err(dev, "PWRREQ1 is invalid control for rail %d\n", id);
+ return -EINVAL;
+ }
+
+ en_bit = deepsleep_data[id].ds_pos_bit;
+ slot_bit = en_bit + 1;
+ ret = rc5t583_read(dev, deepsleep_data[id].reg_add, &sleepseq_val);
+ if (ret < 0) {
+ dev_err(dev, "Error in reading reg 0x%x\n",
+ deepsleep_data[id].reg_add);
+ return ret;
+ }
+
+ sleepseq_val &= ~(0xF << en_bit);
+ sleepseq_val |= BIT(en_bit);
+ sleepseq_val |= ((slots & 0x7) << slot_bit);
+ ret = rc5t583_set_bits(dev, RICOH_ONOFFSEL_REG, BIT(1));
+ if (ret < 0) {
+ dev_err(dev, "Error in updating the 0x%02x register\n",
+ RICOH_ONOFFSEL_REG);
+ return ret;
+ }
+
+ ret = rc5t583_write(dev, deepsleep_data[id].reg_add, sleepseq_val);
+ if (ret < 0) {
+ dev_err(dev, "Error in writing reg 0x%x\n",
+ deepsleep_data[id].reg_add);
+ return ret;
+ }
+
+ if (id == RC5T583_DS_LDO4) {
+ ret = rc5t583_write(dev, RICOH_SWCTL_REG, 0x1);
+ if (ret < 0)
+ dev_err(dev, "Error in writing reg 0x%x\n",
+ RICOH_SWCTL_REG);
+ }
+ return ret;
+}
+
+static int __rc5t583_set_ext_pwrreq2_control(struct device *dev,
+ int id, int ext_pwr)
+{
+ int ret;
+
+ if (id != RC5T583_DS_DC0) {
+ dev_err(dev, "PWRREQ2 is invalid control for rail %d\n", id);
+ return -EINVAL;
+ }
+
+ ret = rc5t583_set_bits(dev, RICOH_ONOFFSEL_REG, BIT(2));
+ if (ret < 0)
+ dev_err(dev, "Error in updating the ONOFFSEL 0x10 register\n");
+ return ret;
+}
+
+int rc5t583_ext_power_req_config(struct device *dev, int ds_id,
+ int ext_pwr_req, int deepsleep_slot_nr)
+{
+ if ((ext_pwr_req & EXT_PWR_REQ) == EXT_PWR_REQ)
+ return -EINVAL;
+
+ if (ext_pwr_req & RC5T583_EXT_PWRREQ1_CONTROL)
+ return __rc5t583_set_ext_pwrreq1_control(dev, ds_id,
+ ext_pwr_req, deepsleep_slot_nr);
+
+ if (ext_pwr_req & RC5T583_EXT_PWRREQ2_CONTROL)
+ return __rc5t583_set_ext_pwrreq2_control(dev,
+ ds_id, ext_pwr_req);
+ return 0;
+}
+
+static int rc5t583_clear_ext_power_req(struct rc5t583 *rc5t583,
+ struct rc5t583_platform_data *pdata)
+{
+ int ret;
+ int i;
+ uint8_t on_off_val = 0;
+
+ /* Clear ONOFFSEL register */
+ if (pdata->enable_shutdown)
+ on_off_val = 0x1;
+
+ ret = rc5t583_write(rc5t583->dev, RICOH_ONOFFSEL_REG, on_off_val);
+ if (ret < 0)
+ dev_warn(rc5t583->dev, "Error in writing reg %d error: %d\n",
+ RICOH_ONOFFSEL_REG, ret);
+
+ ret = rc5t583_write(rc5t583->dev, RICOH_SWCTL_REG, 0x0);
+ if (ret < 0)
+ dev_warn(rc5t583->dev, "Error in writing reg %d error: %d\n",
+ RICOH_SWCTL_REG, ret);
+
+ /* Clear sleep sequence register */
+ for (i = RC5T583_SLPSEQ1; i <= RC5T583_SLPSEQ11; ++i) {
+ ret = rc5t583_write(rc5t583->dev, i, 0x0);
+ if (ret < 0)
+ dev_warn(rc5t583->dev,
+ "Error in writing reg 0x%02x error: %d\n",
+ i, ret);
+ }
+ return 0;
+}
+
+static bool volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* Enable caching in interrupt registers */
+ switch (reg) {
+ case RC5T583_INT_EN_SYS1:
+ case RC5T583_INT_EN_SYS2:
+ case RC5T583_INT_EN_DCDC:
+ case RC5T583_INT_EN_RTC:
+ case RC5T583_INT_EN_ADC1:
+ case RC5T583_INT_EN_ADC2:
+ case RC5T583_INT_EN_ADC3:
+ case RC5T583_GPIO_GPEDGE1:
+ case RC5T583_GPIO_GPEDGE2:
+ case RC5T583_GPIO_EN_INT:
+ return false;
+
+ case RC5T583_GPIO_MON_IOIN:
+ /* This is gpio input register */
+ return true;
+
+ default:
+ /* Enable caching in gpio registers */
+ if ((reg >= RC5T583_GPIO_IOSEL) &&
+ (reg <= RC5T583_GPIO_GPOFUNC))
+ return false;
+
+ /* Enable caching in sleep seq registers */
+ if ((reg >= RC5T583_SLPSEQ1) && (reg <= RC5T583_SLPSEQ11))
+ return false;
+
+ /* Enable caching of regulator registers */
+ if ((reg >= RC5T583_REG_DC0CTL) && (reg <= RC5T583_REG_SR3CTL))
+ return false;
+ if ((reg >= RC5T583_REG_LDOEN1) &&
+ (reg <= RC5T583_REG_LDO9DAC_DS))
+ return false;
+
+ break;
+ }
+
+ return true;
+}
+
+static const struct regmap_config rc5t583_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = volatile_reg,
+ .max_register = RC5T583_MAX_REGS,
+ .num_reg_defaults_raw = RC5T583_MAX_REGS,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rc5t583 *rc5t583;
+ struct rc5t583_platform_data *pdata = i2c->dev.platform_data;
+ int ret;
+ bool irq_init_success = false;
+
+ if (!pdata) {
+ dev_err(&i2c->dev, "Err: Platform data not found\n");
+ return -EINVAL;
+ }
+
+ rc5t583 = devm_kzalloc(&i2c->dev, sizeof(struct rc5t583), GFP_KERNEL);
+ if (!rc5t583) {
+ dev_err(&i2c->dev, "Memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ rc5t583->dev = &i2c->dev;
+ i2c_set_clientdata(i2c, rc5t583);
+
+ rc5t583->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config);
+ if (IS_ERR(rc5t583->regmap)) {
+ ret = PTR_ERR(rc5t583->regmap);
+ dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = rc5t583_clear_ext_power_req(rc5t583, pdata);
+ if (ret < 0)
+ goto err_irq_init;
+
+ if (i2c->irq) {
+ ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base);
+ /* Still continue with waring if irq init fails */
+ if (ret)
+ dev_warn(&i2c->dev, "IRQ init failed: %d\n", ret);
+ else
+ irq_init_success = true;
+ }
+
+ ret = mfd_add_devices(rc5t583->dev, -1, rc5t583_subdevs,
+ ARRAY_SIZE(rc5t583_subdevs), NULL, 0);
+ if (ret) {
+ dev_err(&i2c->dev, "add mfd devices failed: %d\n", ret);
+ goto err_add_devs;
+ }
+
+ return 0;
+
+err_add_devs:
+ if (irq_init_success)
+ rc5t583_irq_exit(rc5t583);
+err_irq_init:
+ regmap_exit(rc5t583->regmap);
+ return ret;
+}
+
+static int __devexit rc5t583_i2c_remove(struct i2c_client *i2c)
+{
+ struct rc5t583 *rc5t583 = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(rc5t583->dev);
+ rc5t583_irq_exit(rc5t583);
+ regmap_exit(rc5t583->regmap);
+ return 0;
+}
+
+static const struct i2c_device_id rc5t583_i2c_id[] = {
+ {.name = "rc5t583", .driver_data = 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, rc5t583_i2c_id);
+
+static struct i2c_driver rc5t583_i2c_driver = {
+ .driver = {
+ .name = "rc5t583",
+ .owner = THIS_MODULE,
+ },
+ .probe = rc5t583_i2c_probe,
+ .remove = __devexit_p(rc5t583_i2c_remove),
+ .id_table = rc5t583_i2c_id,
+};
+
+static int __init rc5t583_i2c_init(void)
+{
+ return i2c_add_driver(&rc5t583_i2c_driver);
+}
+subsys_initcall(rc5t583_i2c_init);
+
+static void __exit rc5t583_i2c_exit(void)
+{
+ i2c_del_driver(&rc5t583_i2c_driver);
+}
+
+module_exit(rc5t583_i2c_exit);
+
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("RICOH RC5T583 power management system device driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c
index caadabe..48949d9 100644
--- a/drivers/mfd/s5m-core.c
+++ b/drivers/mfd/s5m-core.c
@@ -26,7 +26,27 @@
#include <linux/mfd/s5m87xx/s5m-rtc.h>
#include <linux/regmap.h>
-static struct mfd_cell s5m87xx_devs[] = {
+static struct mfd_cell s5m8751_devs[] = {
+ {
+ .name = "s5m8751-pmic",
+ }, {
+ .name = "s5m-charger",
+ }, {
+ .name = "s5m8751-codec",
+ },
+};
+
+static struct mfd_cell s5m8763_devs[] = {
+ {
+ .name = "s5m8763-pmic",
+ }, {
+ .name = "s5m-rtc",
+ }, {
+ .name = "s5m-charger",
+ },
+};
+
+static struct mfd_cell s5m8767_devs[] = {
{
.name = "s5m8767-pmic",
}, {
@@ -42,7 +62,7 @@ EXPORT_SYMBOL_GPL(s5m_reg_read);
int s5m_bulk_read(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)
{
- return regmap_bulk_read(s5m87xx->regmap, reg, buf, count);;
+ return regmap_bulk_read(s5m87xx->regmap, reg, buf, count);
}
EXPORT_SYMBOL_GPL(s5m_bulk_read);
@@ -54,7 +74,7 @@ EXPORT_SYMBOL_GPL(s5m_reg_write);
int s5m_bulk_write(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)
{
- return regmap_raw_write(s5m87xx->regmap, reg, buf, count * sizeof(u16));
+ return regmap_raw_write(s5m87xx->regmap, reg, buf, count);
}
EXPORT_SYMBOL_GPL(s5m_bulk_write);
@@ -74,10 +94,10 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c,
{
struct s5m_platform_data *pdata = i2c->dev.platform_data;
struct s5m87xx_dev *s5m87xx;
- int ret = 0;
- int error;
+ int ret;
- s5m87xx = kzalloc(sizeof(struct s5m87xx_dev), GFP_KERNEL);
+ s5m87xx = devm_kzalloc(&i2c->dev, sizeof(struct s5m87xx_dev),
+ GFP_KERNEL);
if (s5m87xx == NULL)
return -ENOMEM;
@@ -96,9 +116,9 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c,
s5m87xx->regmap = regmap_init_i2c(i2c, &s5m_regmap_config);
if (IS_ERR(s5m87xx->regmap)) {
- error = PTR_ERR(s5m87xx->regmap);
+ ret = PTR_ERR(s5m87xx->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
- error);
+ ret);
goto err;
}
@@ -112,9 +132,23 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c,
pm_runtime_set_active(s5m87xx->dev);
- ret = mfd_add_devices(s5m87xx->dev, -1,
- s5m87xx_devs, ARRAY_SIZE(s5m87xx_devs),
- NULL, 0);
+ switch (s5m87xx->device_type) {
+ case S5M8751X:
+ ret = mfd_add_devices(s5m87xx->dev, -1, s5m8751_devs,
+ ARRAY_SIZE(s5m8751_devs), NULL, 0);
+ break;
+ case S5M8763X:
+ ret = mfd_add_devices(s5m87xx->dev, -1, s5m8763_devs,
+ ARRAY_SIZE(s5m8763_devs), NULL, 0);
+ break;
+ case S5M8767X:
+ ret = mfd_add_devices(s5m87xx->dev, -1, s5m8767_devs,
+ ARRAY_SIZE(s5m8767_devs), NULL, 0);
+ break;
+ default:
+ /* If this happens the probe function is problem */
+ BUG();
+ }
if (ret < 0)
goto err;
@@ -126,7 +160,6 @@ err:
s5m_irq_exit(s5m87xx);
i2c_unregister_device(s5m87xx->rtc);
regmap_exit(s5m87xx->regmap);
- kfree(s5m87xx);
return ret;
}
@@ -138,7 +171,6 @@ static int s5m87xx_i2c_remove(struct i2c_client *i2c)
s5m_irq_exit(s5m87xx);
i2c_unregister_device(s5m87xx->rtc);
regmap_exit(s5m87xx->regmap);
- kfree(s5m87xx);
return 0;
}
diff --git a/drivers/mfd/s5m-irq.c b/drivers/mfd/s5m-irq.c
index de76dfb..0236676 100644
--- a/drivers/mfd/s5m-irq.c
+++ b/drivers/mfd/s5m-irq.c
@@ -342,7 +342,10 @@ int s5m_irq_resume(struct s5m87xx_dev *s5m87xx)
s5m8767_irq_thread(s5m87xx->irq_base, s5m87xx);
break;
default:
- break;
+ dev_err(s5m87xx->dev,
+ "Unknown device type %d\n",
+ s5m87xx->device_type);
+ return -EINVAL;
}
}
@@ -444,7 +447,9 @@ int s5m_irq_init(struct s5m87xx_dev *s5m87xx)
}
break;
default:
- break;
+ dev_err(s5m87xx->dev,
+ "Unknown device type %d\n", s5m87xx->device_type);
+ return -EINVAL;
}
if (!s5m87xx->ono)
@@ -467,12 +472,15 @@ int s5m_irq_init(struct s5m87xx_dev *s5m87xx)
IRQF_ONESHOT, "s5m87xx-ono", s5m87xx);
break;
default:
+ ret = -EINVAL;
break;
}
- if (ret)
+ if (ret) {
dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
s5m87xx->ono, ret);
+ return ret;
+ }
return 0;
}
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
index f4d8611..d927dd4 100644
--- a/drivers/mfd/sm501.c
+++ b/drivers/mfd/sm501.c
@@ -387,14 +387,6 @@ int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to)
EXPORT_SYMBOL_GPL(sm501_unit_power);
-
-/* Perform a rounded division. */
-static long sm501fb_round_div(long num, long denom)
-{
- /* n / d + 1 / 2 = (2n + d) / 2d */
- return (2 * num + denom) / (2 * denom);
-}
-
/* clock value structure. */
struct sm501_clock {
unsigned long mclk;
@@ -428,7 +420,7 @@ static int sm501_calc_clock(unsigned long freq,
/* try all 8 shift values.*/
for (shift = 0; shift < 8; shift++) {
/* Calculate difference to requested clock */
- diff = sm501fb_round_div(mclk, divider << shift) - freq;
+ diff = DIV_ROUND_CLOSEST(mclk, divider << shift) - freq;
if (diff < 0)
diff = -diff;
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index e07947e..2dd8d49 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -298,6 +298,11 @@ static struct mfd_cell stmpe_gpio_cell = {
.num_resources = ARRAY_SIZE(stmpe_gpio_resources),
};
+static struct mfd_cell stmpe_gpio_cell_noirq = {
+ .name = "stmpe-gpio",
+ /* gpio cell resources consist of an irq only so no resources here */
+};
+
/*
* Keypad (1601, 2401, 2403)
*/
@@ -346,6 +351,13 @@ static struct stmpe_variant_block stmpe801_blocks[] = {
},
};
+static struct stmpe_variant_block stmpe801_blocks_noirq[] = {
+ {
+ .cell = &stmpe_gpio_cell_noirq,
+ .block = STMPE_BLOCK_GPIO,
+ },
+};
+
static int stmpe801_enable(struct stmpe *stmpe, unsigned int blocks,
bool enable)
{
@@ -367,6 +379,17 @@ static struct stmpe_variant_info stmpe801 = {
.enable = stmpe801_enable,
};
+static struct stmpe_variant_info stmpe801_noirq = {
+ .name = "stmpe801",
+ .id_val = STMPE801_ID,
+ .id_mask = 0xffff,
+ .num_gpios = 8,
+ .regs = stmpe801_regs,
+ .blocks = stmpe801_blocks_noirq,
+ .num_blocks = ARRAY_SIZE(stmpe801_blocks_noirq),
+ .enable = stmpe801_enable,
+};
+
/*
* Touchscreen (STMPE811 or STMPE610)
*/
@@ -712,7 +735,7 @@ static struct stmpe_variant_info stmpe2403 = {
.enable_autosleep = stmpe1601_autosleep, /* same as stmpe1601 */
};
-static struct stmpe_variant_info *stmpe_variant_info[] = {
+static struct stmpe_variant_info *stmpe_variant_info[STMPE_NBR_PARTS] = {
[STMPE610] = &stmpe610,
[STMPE801] = &stmpe801,
[STMPE811] = &stmpe811,
@@ -721,6 +744,16 @@ static struct stmpe_variant_info *stmpe_variant_info[] = {
[STMPE2403] = &stmpe2403,
};
+/*
+ * These devices can be connected in a 'no-irq' configuration - the irq pin
+ * is not used and the device cannot interrupt the CPU. Here we only list
+ * devices which support this configuration - the driver will fail probing
+ * for any devices not listed here which are configured in this way.
+ */
+static struct stmpe_variant_info *stmpe_noirq_variant_info[STMPE_NBR_PARTS] = {
+ [STMPE801] = &stmpe801_noirq,
+};
+
static irqreturn_t stmpe_irq(int irq, void *data)
{
struct stmpe *stmpe = data;
@@ -864,7 +897,7 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe)
unsigned int irq_trigger = stmpe->pdata->irq_trigger;
int autosleep_timeout = stmpe->pdata->autosleep_timeout;
struct stmpe_variant_info *variant = stmpe->variant;
- u8 icr;
+ u8 icr = 0;
unsigned int id;
u8 data[2];
int ret;
@@ -887,31 +920,33 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe)
if (ret)
return ret;
- if (id == STMPE801_ID)
- icr = STMPE801_REG_SYS_CTRL_INT_EN;
- else
- icr = STMPE_ICR_LSB_GIM;
-
- /* STMPE801 doesn't support Edge interrupts */
- if (id != STMPE801_ID) {
- if (irq_trigger == IRQF_TRIGGER_FALLING ||
- irq_trigger == IRQF_TRIGGER_RISING)
- icr |= STMPE_ICR_LSB_EDGE;
- }
-
- if (irq_trigger == IRQF_TRIGGER_RISING ||
- irq_trigger == IRQF_TRIGGER_HIGH) {
+ if (stmpe->irq >= 0) {
if (id == STMPE801_ID)
- icr |= STMPE801_REG_SYS_CTRL_INT_HI;
+ icr = STMPE801_REG_SYS_CTRL_INT_EN;
else
- icr |= STMPE_ICR_LSB_HIGH;
- }
+ icr = STMPE_ICR_LSB_GIM;
- if (stmpe->pdata->irq_invert_polarity) {
- if (id == STMPE801_ID)
- icr ^= STMPE801_REG_SYS_CTRL_INT_HI;
- else
- icr ^= STMPE_ICR_LSB_HIGH;
+ /* STMPE801 doesn't support Edge interrupts */
+ if (id != STMPE801_ID) {
+ if (irq_trigger == IRQF_TRIGGER_FALLING ||
+ irq_trigger == IRQF_TRIGGER_RISING)
+ icr |= STMPE_ICR_LSB_EDGE;
+ }
+
+ if (irq_trigger == IRQF_TRIGGER_RISING ||
+ irq_trigger == IRQF_TRIGGER_HIGH) {
+ if (id == STMPE801_ID)
+ icr |= STMPE801_REG_SYS_CTRL_INT_HI;
+ else
+ icr |= STMPE_ICR_LSB_HIGH;
+ }
+
+ if (stmpe->pdata->irq_invert_polarity) {
+ if (id == STMPE801_ID)
+ icr ^= STMPE801_REG_SYS_CTRL_INT_HI;
+ else
+ icr ^= STMPE_ICR_LSB_HIGH;
+ }
}
if (stmpe->pdata->autosleep) {
@@ -1001,19 +1036,38 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)
stmpe->irq = ci->irq;
}
+ if (stmpe->irq < 0) {
+ /* use alternate variant info for no-irq mode, if supported */
+ dev_info(stmpe->dev,
+ "%s configured in no-irq mode by platform data\n",
+ stmpe->variant->name);
+ if (!stmpe_noirq_variant_info[stmpe->partnum]) {
+ dev_err(stmpe->dev,
+ "%s does not support no-irq mode!\n",
+ stmpe->variant->name);
+ ret = -ENODEV;
+ goto free_gpio;
+ }
+ stmpe->variant = stmpe_noirq_variant_info[stmpe->partnum];
+ }
+
ret = stmpe_chip_init(stmpe);
if (ret)
goto free_gpio;
- ret = stmpe_irq_init(stmpe);
- if (ret)
- goto free_gpio;
+ if (stmpe->irq >= 0) {
+ ret = stmpe_irq_init(stmpe);
+ if (ret)
+ goto free_gpio;
- ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq,
- pdata->irq_trigger | IRQF_ONESHOT, "stmpe", stmpe);
- if (ret) {
- dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret);
- goto out_removeirq;
+ ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq,
+ pdata->irq_trigger | IRQF_ONESHOT,
+ "stmpe", stmpe);
+ if (ret) {
+ dev_err(stmpe->dev, "failed to request IRQ: %d\n",
+ ret);
+ goto out_removeirq;
+ }
}
ret = stmpe_devices_init(stmpe);
@@ -1026,9 +1080,11 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)
out_removedevs:
mfd_remove_devices(stmpe->dev);
- free_irq(stmpe->irq, stmpe);
+ if (stmpe->irq >= 0)
+ free_irq(stmpe->irq, stmpe);
out_removeirq:
- stmpe_irq_remove(stmpe);
+ if (stmpe->irq >= 0)
+ stmpe_irq_remove(stmpe);
free_gpio:
if (pdata->irq_over_gpio)
gpio_free(pdata->irq_gpio);
@@ -1041,8 +1097,10 @@ int stmpe_remove(struct stmpe *stmpe)
{
mfd_remove_devices(stmpe->dev);
- free_irq(stmpe->irq, stmpe);
- stmpe_irq_remove(stmpe);
+ if (stmpe->irq >= 0) {
+ free_irq(stmpe->irq, stmpe);
+ stmpe_irq_remove(stmpe);
+ }
if (stmpe->pdata->irq_over_gpio)
gpio_free(stmpe->pdata->irq_gpio);
@@ -1057,7 +1115,7 @@ static int stmpe_suspend(struct device *dev)
{
struct stmpe *stmpe = dev_get_drvdata(dev);
- if (device_may_wakeup(dev))
+ if (stmpe->irq >= 0 && device_may_wakeup(dev))
enable_irq_wake(stmpe->irq);
return 0;
@@ -1067,7 +1125,7 @@ static int stmpe_resume(struct device *dev)
{
struct stmpe *stmpe = dev_get_drvdata(dev);
- if (device_may_wakeup(dev))
+ if (stmpe->irq >= 0 && device_may_wakeup(dev))
disable_irq_wake(stmpe->irq);
return 0;
diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c
new file mode 100644
index 0000000..a66d4df
--- /dev/null
+++ b/drivers/mfd/tps65090.c
@@ -0,0 +1,387 @@
+/*
+ * Core driver for TI TPS65090 PMIC family
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65090.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+
+#define NUM_INT_REG 2
+#define TOTAL_NUM_REG 0x18
+
+/* interrupt status registers */
+#define TPS65090_INT_STS 0x0
+#define TPS65090_INT_STS2 0x1
+
+/* interrupt mask registers */
+#define TPS65090_INT_MSK 0x2
+#define TPS65090_INT_MSK2 0x3
+
+struct tps65090_irq_data {
+ u8 mask_reg;
+ u8 mask_pos;
+};
+
+#define TPS65090_IRQ(_reg, _mask_pos) \
+ { \
+ .mask_reg = (_reg), \
+ .mask_pos = (_mask_pos), \
+ }
+
+static const struct tps65090_irq_data tps65090_irqs[] = {
+ [0] = TPS65090_IRQ(0, 0),
+ [1] = TPS65090_IRQ(0, 1),
+ [2] = TPS65090_IRQ(0, 2),
+ [3] = TPS65090_IRQ(0, 3),
+ [4] = TPS65090_IRQ(0, 4),
+ [5] = TPS65090_IRQ(0, 5),
+ [6] = TPS65090_IRQ(0, 6),
+ [7] = TPS65090_IRQ(0, 7),
+ [8] = TPS65090_IRQ(1, 0),
+ [9] = TPS65090_IRQ(1, 1),
+ [10] = TPS65090_IRQ(1, 2),
+ [11] = TPS65090_IRQ(1, 3),
+ [12] = TPS65090_IRQ(1, 4),
+ [13] = TPS65090_IRQ(1, 5),
+ [14] = TPS65090_IRQ(1, 6),
+ [15] = TPS65090_IRQ(1, 7),
+};
+
+static struct mfd_cell tps65090s[] = {
+ {
+ .name = "tps65910-pmic",
+ },
+ {
+ .name = "tps65910-regulator",
+ },
+};
+
+struct tps65090 {
+ struct mutex lock;
+ struct device *dev;
+ struct i2c_client *client;
+ struct regmap *rmap;
+ struct irq_chip irq_chip;
+ struct mutex irq_lock;
+ int irq_base;
+ unsigned int id;
+};
+
+int tps65090_write(struct device *dev, int reg, uint8_t val)
+{
+ struct tps65090 *tps = dev_get_drvdata(dev);
+ return regmap_write(tps->rmap, reg, val);
+}
+EXPORT_SYMBOL_GPL(tps65090_write);
+
+int tps65090_read(struct device *dev, int reg, uint8_t *val)
+{
+ struct tps65090 *tps = dev_get_drvdata(dev);
+ unsigned int temp_val;
+ int ret;
+ ret = regmap_read(tps->rmap, reg, &temp_val);
+ if (!ret)
+ *val = temp_val;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tps65090_read);
+
+int tps65090_set_bits(struct device *dev, int reg, uint8_t bit_num)
+{
+ struct tps65090 *tps = dev_get_drvdata(dev);
+ return regmap_update_bits(tps->rmap, reg, BIT(bit_num), ~0u);
+}
+EXPORT_SYMBOL_GPL(tps65090_set_bits);
+
+int tps65090_clr_bits(struct device *dev, int reg, uint8_t bit_num)
+{
+ struct tps65090 *tps = dev_get_drvdata(dev);
+ return regmap_update_bits(tps->rmap, reg, BIT(bit_num), 0u);
+}
+EXPORT_SYMBOL_GPL(tps65090_clr_bits);
+
+static void tps65090_irq_lock(struct irq_data *data)
+{
+ struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&tps65090->irq_lock);
+}
+
+static void tps65090_irq_mask(struct irq_data *irq_data)
+{
+ struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data);
+ unsigned int __irq = irq_data->hwirq;
+ const struct tps65090_irq_data *data = &tps65090_irqs[__irq];
+
+ tps65090_set_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg),
+ data->mask_pos);
+}
+
+static void tps65090_irq_unmask(struct irq_data *irq_data)
+{
+ struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data);
+ unsigned int __irq = irq_data->irq - tps65090->irq_base;
+ const struct tps65090_irq_data *data = &tps65090_irqs[__irq];
+
+ tps65090_clr_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg),
+ data->mask_pos);
+}
+
+static void tps65090_irq_sync_unlock(struct irq_data *data)
+{
+ struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data);
+
+ mutex_unlock(&tps65090->irq_lock);
+}
+
+static irqreturn_t tps65090_irq(int irq, void *data)
+{
+ struct tps65090 *tps65090 = data;
+ int ret = 0;
+ u8 status, mask;
+ unsigned long int acks = 0;
+ int i;
+
+ for (i = 0; i < NUM_INT_REG; i++) {
+ ret = tps65090_read(tps65090->dev, TPS65090_INT_MSK + i, &mask);
+ if (ret < 0) {
+ dev_err(tps65090->dev,
+ "failed to read mask reg [addr:%d]\n",
+ TPS65090_INT_MSK + i);
+ return IRQ_NONE;
+ }
+ ret = tps65090_read(tps65090->dev, TPS65090_INT_STS + i,
+ &status);
+ if (ret < 0) {
+ dev_err(tps65090->dev,
+ "failed to read status reg [addr:%d]\n",
+ TPS65090_INT_STS + i);
+ return IRQ_NONE;
+ }
+ if (status) {
+ /* Ack only those interrupts which are not masked */
+ status &= (~mask);
+ ret = tps65090_write(tps65090->dev,
+ TPS65090_INT_STS + i, status);
+ if (ret < 0) {
+ dev_err(tps65090->dev,
+ "failed to write interrupt status\n");
+ return IRQ_NONE;
+ }
+ acks |= (status << (i * 8));
+ }
+ }
+
+ for_each_set_bit(i, &acks, ARRAY_SIZE(tps65090_irqs))
+ handle_nested_irq(tps65090->irq_base + i);
+ return acks ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int __devinit tps65090_irq_init(struct tps65090 *tps65090, int irq,
+ int irq_base)
+{
+ int i, ret;
+
+ if (!irq_base) {
+ dev_err(tps65090->dev, "IRQ base not set\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&tps65090->irq_lock);
+
+ for (i = 0; i < NUM_INT_REG; i++)
+ tps65090_write(tps65090->dev, TPS65090_INT_MSK + i, 0xFF);
+
+ for (i = 0; i < NUM_INT_REG; i++)
+ tps65090_write(tps65090->dev, TPS65090_INT_STS + i, 0xff);
+
+ tps65090->irq_base = irq_base;
+ tps65090->irq_chip.name = "tps65090";
+ tps65090->irq_chip.irq_mask = tps65090_irq_mask;
+ tps65090->irq_chip.irq_unmask = tps65090_irq_unmask;
+ tps65090->irq_chip.irq_bus_lock = tps65090_irq_lock;
+ tps65090->irq_chip.irq_bus_sync_unlock = tps65090_irq_sync_unlock;
+
+ for (i = 0; i < ARRAY_SIZE(tps65090_irqs); i++) {
+ int __irq = i + tps65090->irq_base;
+ irq_set_chip_data(__irq, tps65090);
+ irq_set_chip_and_handler(__irq, &tps65090->irq_chip,
+ handle_simple_irq);
+ irq_set_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(__irq, IRQF_VALID);
+#endif
+ }
+
+ ret = request_threaded_irq(irq, NULL, tps65090_irq, IRQF_ONESHOT,
+ "tps65090", tps65090);
+ if (!ret) {
+ device_init_wakeup(tps65090->dev, 1);
+ enable_irq_wake(irq);
+ }
+
+ return ret;
+}
+
+static bool is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS))
+ return true;
+ else
+ return false;
+}
+
+static const struct regmap_config tps65090_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TOTAL_NUM_REG,
+ .num_reg_defaults_raw = TOTAL_NUM_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = is_volatile_reg,
+};
+
+static int __devinit tps65090_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tps65090_platform_data *pdata = client->dev.platform_data;
+ struct tps65090 *tps65090;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&client->dev, "tps65090 requires platform data\n");
+ return -EINVAL;
+ }
+
+ tps65090 = devm_kzalloc(&client->dev, sizeof(struct tps65090),
+ GFP_KERNEL);
+ if (tps65090 == NULL)
+ return -ENOMEM;
+
+ tps65090->client = client;
+ tps65090->dev = &client->dev;
+ i2c_set_clientdata(client, tps65090);
+
+ mutex_init(&tps65090->lock);
+
+ if (client->irq) {
+ ret = tps65090_irq_init(tps65090, client->irq, pdata->irq_base);
+ if (ret) {
+ dev_err(&client->dev, "IRQ init failed with err: %d\n",
+ ret);
+ goto err_exit;
+ }
+ }
+
+ tps65090->rmap = regmap_init_i2c(tps65090->client,
+ &tps65090_regmap_config);
+ if (IS_ERR(tps65090->rmap)) {
+ dev_err(&client->dev, "regmap_init failed with err: %ld\n",
+ PTR_ERR(tps65090->rmap));
+ goto err_irq_exit;
+ };
+
+ ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
+ ARRAY_SIZE(tps65090s), NULL, 0);
+ if (ret) {
+ dev_err(&client->dev, "add mfd devices failed with err: %d\n",
+ ret);
+ goto err_regmap_exit;
+ }
+
+ return 0;
+
+err_regmap_exit:
+ regmap_exit(tps65090->rmap);
+
+err_irq_exit:
+ if (client->irq)
+ free_irq(client->irq, tps65090);
+err_exit:
+ return ret;
+}
+
+static int __devexit tps65090_i2c_remove(struct i2c_client *client)
+{
+ struct tps65090 *tps65090 = i2c_get_clientdata(client);
+
+ mfd_remove_devices(tps65090->dev);
+ regmap_exit(tps65090->rmap);
+ if (client->irq)
+ free_irq(client->irq, tps65090);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tps65090_i2c_suspend(struct i2c_client *client, pm_message_t state)
+{
+ if (client->irq)
+ disable_irq(client->irq);
+ return 0;
+}
+
+static int tps65090_i2c_resume(struct i2c_client *client)
+{
+ if (client->irq)
+ enable_irq(client->irq);
+ return 0;
+}
+#endif
+
+static const struct i2c_device_id tps65090_id_table[] = {
+ { "tps65090", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, tps65090_id_table);
+
+static struct i2c_driver tps65090_driver = {
+ .driver = {
+ .name = "tps65090",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps65090_i2c_probe,
+ .remove = __devexit_p(tps65090_i2c_remove),
+#ifdef CONFIG_PM
+ .suspend = tps65090_i2c_suspend,
+ .resume = tps65090_i2c_resume,
+#endif
+ .id_table = tps65090_id_table,
+};
+
+static int __init tps65090_init(void)
+{
+ return i2c_add_driver(&tps65090_driver);
+}
+subsys_initcall(tps65090_init);
+
+static void __exit tps65090_exit(void)
+{
+ i2c_del_driver(&tps65090_driver);
+}
+module_exit(tps65090_exit);
+
+MODULE_DESCRIPTION("TPS65090 core driver");
+MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c
new file mode 100644
index 0000000..f7d854e
--- /dev/null
+++ b/drivers/mfd/tps65217.c
@@ -0,0 +1,242 @@
+/*
+ * tps65217.c
+ *
+ * TPS65217 chip family multi-function driver
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65217.h>
+
+/**
+ * tps65217_reg_read: Read a single tps65217 register.
+ *
+ * @tps: Device to read from.
+ * @reg: Register to read.
+ * @val: Contians the value
+ */
+int tps65217_reg_read(struct tps65217 *tps, unsigned int reg,
+ unsigned int *val)
+{
+ return regmap_read(tps->regmap, reg, val);
+}
+EXPORT_SYMBOL_GPL(tps65217_reg_read);
+
+/**
+ * tps65217_reg_write: Write a single tps65217 register.
+ *
+ * @tps65217: Device to write to.
+ * @reg: Register to write to.
+ * @val: Value to write.
+ * @level: Password protected level
+ */
+int tps65217_reg_write(struct tps65217 *tps, unsigned int reg,
+ unsigned int val, unsigned int level)
+{
+ int ret;
+ unsigned int xor_reg_val;
+
+ switch (level) {
+ case TPS65217_PROTECT_NONE:
+ return regmap_write(tps->regmap, reg, val);
+ case TPS65217_PROTECT_L1:
+ xor_reg_val = reg ^ TPS65217_PASSWORD_REGS_UNLOCK;
+ ret = regmap_write(tps->regmap, TPS65217_REG_PASSWORD,
+ xor_reg_val);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write(tps->regmap, reg, val);
+ case TPS65217_PROTECT_L2:
+ xor_reg_val = reg ^ TPS65217_PASSWORD_REGS_UNLOCK;
+ ret = regmap_write(tps->regmap, TPS65217_REG_PASSWORD,
+ xor_reg_val);
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(tps->regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(tps->regmap, TPS65217_REG_PASSWORD,
+ xor_reg_val);
+ if (ret < 0)
+ return ret;
+ return regmap_write(tps->regmap, reg, val);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(tps65217_reg_write);
+
+/**
+ * tps65217_update_bits: Modify bits w.r.t mask, val and level.
+ *
+ * @tps65217: Device to write to.
+ * @reg: Register to read-write to.
+ * @mask: Mask.
+ * @val: Value to write.
+ * @level: Password protected level
+ */
+int tps65217_update_bits(struct tps65217 *tps, unsigned int reg,
+ unsigned int mask, unsigned int val, unsigned int level)
+{
+ int ret;
+ unsigned int data;
+
+ ret = tps65217_reg_read(tps, reg, &data);
+ if (ret) {
+ dev_err(tps->dev, "Read from reg 0x%x failed\n", reg);
+ return ret;
+ }
+
+ data &= ~mask;
+ data |= val & mask;
+
+ ret = tps65217_reg_write(tps, reg, data, level);
+ if (ret)
+ dev_err(tps->dev, "Write for reg 0x%x failed\n", reg);
+
+ return ret;
+}
+
+int tps65217_set_bits(struct tps65217 *tps, unsigned int reg,
+ unsigned int mask, unsigned int val, unsigned int level)
+{
+ return tps65217_update_bits(tps, reg, mask, val, level);
+}
+EXPORT_SYMBOL_GPL(tps65217_set_bits);
+
+int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg,
+ unsigned int mask, unsigned int level)
+{
+ return tps65217_update_bits(tps, reg, mask, 0, level);
+}
+EXPORT_SYMBOL_GPL(tps65217_clear_bits);
+
+static struct regmap_config tps65217_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int __devinit tps65217_probe(struct i2c_client *client,
+ const struct i2c_device_id *ids)
+{
+ struct tps65217 *tps;
+ struct tps65217_board *pdata = client->dev.platform_data;
+ int i, ret;
+ unsigned int version;
+
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ tps->pdata = pdata;
+ tps->regmap = regmap_init_i2c(client, &tps65217_regmap_config);
+ if (IS_ERR(tps->regmap)) {
+ ret = PTR_ERR(tps->regmap);
+ dev_err(tps->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, tps);
+ tps->dev = &client->dev;
+
+ ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version);
+ if (ret < 0) {
+ dev_err(tps->dev, "Failed to read revision"
+ " register: %d\n", ret);
+ goto err_regmap;
+ }
+
+ dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n",
+ (version & TPS65217_CHIPID_CHIP_MASK) >> 4,
+ version & TPS65217_CHIPID_REV_MASK);
+
+ for (i = 0; i < TPS65217_NUM_REGULATOR; i++) {
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("tps65217-pmic", i);
+ if (!pdev) {
+ dev_err(tps->dev, "Cannot create regulator %d\n", i);
+ continue;
+ }
+
+ pdev->dev.parent = tps->dev;
+ platform_device_add_data(pdev, &pdata->tps65217_init_data[i],
+ sizeof(pdata->tps65217_init_data[i]));
+ tps->regulator_pdev[i] = pdev;
+
+ platform_device_add(pdev);
+ }
+
+ return 0;
+
+err_regmap:
+ regmap_exit(tps->regmap);
+
+ return ret;
+}
+
+static int __devexit tps65217_remove(struct i2c_client *client)
+{
+ struct tps65217 *tps = i2c_get_clientdata(client);
+ int i;
+
+ for (i = 0; i < TPS65217_NUM_REGULATOR; i++)
+ platform_device_unregister(tps->regulator_pdev[i]);
+
+ regmap_exit(tps->regmap);
+
+ return 0;
+}
+
+static const struct i2c_device_id tps65217_id_table[] = {
+ {"tps65217", 0xF0},
+ {/* end of list */}
+};
+MODULE_DEVICE_TABLE(i2c, tps65217_id_table);
+
+static struct i2c_driver tps65217_driver = {
+ .driver = {
+ .name = "tps65217",
+ },
+ .id_table = tps65217_id_table,
+ .probe = tps65217_probe,
+ .remove = __devexit_p(tps65217_remove),
+};
+
+static int __init tps65217_init(void)
+{
+ return i2c_add_driver(&tps65217_driver);
+}
+subsys_initcall(tps65217_init);
+
+static void __exit tps65217_exit(void)
+{
+ i2c_del_driver(&tps65217_driver);
+}
+module_exit(tps65217_exit);
+
+MODULE_AUTHOR("AnilKumar Ch <anilkumar@ti.com>");
+MODULE_DESCRIPTION("TPS65217 chip family multi-function driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c
index 95c0d79..c9ed5c0 100644
--- a/drivers/mfd/tps65910-irq.c
+++ b/drivers/mfd/tps65910-irq.c
@@ -145,12 +145,23 @@ static void tps65910_irq_disable(struct irq_data *data)
tps65910->irq_mask |= ( 1 << irq_to_tps65910_irq(tps65910, data->irq));
}
+#ifdef CONFIG_PM_SLEEP
+static int tps65910_irq_set_wake(struct irq_data *data, unsigned int enable)
+{
+ struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
+ return irq_set_irq_wake(tps65910->chip_irq, enable);
+}
+#else
+#define tps65910_irq_set_wake NULL
+#endif
+
static struct irq_chip tps65910_irq_chip = {
.name = "tps65910",
.irq_bus_lock = tps65910_irq_lock,
.irq_bus_sync_unlock = tps65910_irq_sync_unlock,
.irq_disable = tps65910_irq_disable,
.irq_enable = tps65910_irq_enable,
+ .irq_set_wake = tps65910_irq_set_wake,
};
int tps65910_irq_init(struct tps65910 *tps65910, int irq,
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
index 4392f6b..bf2b25e 100644
--- a/drivers/mfd/tps65910.c
+++ b/drivers/mfd/tps65910.c
@@ -16,10 +16,12 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
+#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
+#include <linux/regmap.h>
#include <linux/mfd/tps65910.h>
static struct mfd_cell tps65910s[] = {
@@ -38,99 +40,56 @@ static struct mfd_cell tps65910s[] = {
static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg,
int bytes, void *dest)
{
- struct i2c_client *i2c = tps65910->i2c_client;
- struct i2c_msg xfer[2];
- int ret;
-
- /* Write register */
- xfer[0].addr = i2c->addr;
- xfer[0].flags = 0;
- xfer[0].len = 1;
- xfer[0].buf = &reg;
-
- /* Read data */
- xfer[1].addr = i2c->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = bytes;
- xfer[1].buf = dest;
-
- ret = i2c_transfer(i2c->adapter, xfer, 2);
- if (ret == 2)
- ret = 0;
- else if (ret >= 0)
- ret = -EIO;
-
- return ret;
+ return regmap_bulk_read(tps65910->regmap, reg, dest, bytes);
}
static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg,
- int bytes, void *src)
+ int bytes, void *src)
{
- struct i2c_client *i2c = tps65910->i2c_client;
- /* we add 1 byte for device register */
- u8 msg[TPS65910_MAX_REGISTER + 1];
- int ret;
-
- if (bytes > TPS65910_MAX_REGISTER)
- return -EINVAL;
-
- msg[0] = reg;
- memcpy(&msg[1], src, bytes);
-
- ret = i2c_master_send(i2c, msg, bytes + 1);
- if (ret < 0)
- return ret;
- if (ret != bytes + 1)
- return -EIO;
- return 0;
+ return regmap_bulk_write(tps65910->regmap, reg, src, bytes);
}
int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
{
- u8 data;
- int err;
-
- mutex_lock(&tps65910->io_mutex);
- err = tps65910_i2c_read(tps65910, reg, 1, &data);
- if (err) {
- dev_err(tps65910->dev, "read from reg %x failed\n", reg);
- goto out;
- }
-
- data |= mask;
- err = tps65910_i2c_write(tps65910, reg, 1, &data);
- if (err)
- dev_err(tps65910->dev, "write to reg %x failed\n", reg);
-
-out:
- mutex_unlock(&tps65910->io_mutex);
- return err;
+ return regmap_update_bits(tps65910->regmap, reg, mask, mask);
}
EXPORT_SYMBOL_GPL(tps65910_set_bits);
int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
{
- u8 data;
- int err;
-
- mutex_lock(&tps65910->io_mutex);
- err = tps65910_i2c_read(tps65910, reg, 1, &data);
- if (err) {
- dev_err(tps65910->dev, "read from reg %x failed\n", reg);
- goto out;
- }
-
- data &= ~mask;
- err = tps65910_i2c_write(tps65910, reg, 1, &data);
- if (err)
- dev_err(tps65910->dev, "write to reg %x failed\n", reg);
-
-out:
- mutex_unlock(&tps65910->io_mutex);
- return err;
+ return regmap_update_bits(tps65910->regmap, reg, mask, 0);
}
EXPORT_SYMBOL_GPL(tps65910_clear_bits);
+static bool is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ struct tps65910 *tps65910 = dev_get_drvdata(dev);
+
+ /*
+ * Caching all regulator registers.
+ * All regualator register address range is same for
+ * TPS65910 and TPS65911
+ */
+ if ((reg >= TPS65910_VIO) && (reg <= TPS65910_VDAC)) {
+ /* Check for non-existing register */
+ if (tps65910_chip_id(tps65910) == TPS65910)
+ if ((reg == TPS65911_VDDCTRL_OP) ||
+ (reg == TPS65911_VDDCTRL_SR))
+ return true;
+ return false;
+ }
+ return true;
+}
+
+static const struct regmap_config tps65910_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = is_volatile_reg,
+ .max_register = TPS65910_MAX_REGISTER,
+ .num_reg_defaults_raw = TPS65910_MAX_REGISTER,
+ .cache_type = REGCACHE_RBTREE,
+};
+
static int tps65910_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -161,6 +120,13 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,
tps65910->write = tps65910_i2c_write;
mutex_init(&tps65910->io_mutex);
+ tps65910->regmap = regmap_init_i2c(i2c, &tps65910_regmap_config);
+ if (IS_ERR(tps65910->regmap)) {
+ ret = PTR_ERR(tps65910->regmap);
+ dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
+ goto regmap_err;
+ }
+
ret = mfd_add_devices(tps65910->dev, -1,
tps65910s, ARRAY_SIZE(tps65910s),
NULL, 0);
@@ -178,6 +144,8 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,
return ret;
err:
+ regmap_exit(tps65910->regmap);
+regmap_err:
kfree(tps65910);
kfree(init_data);
return ret;
@@ -189,6 +157,7 @@ static int tps65910_i2c_remove(struct i2c_client *i2c)
tps65910_irq_exit(tps65910);
mfd_remove_devices(tps65910->dev);
+ regmap_exit(tps65910->regmap);
kfree(tps65910);
return 0;
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 806680d..7c2267e 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -46,9 +46,7 @@
#include <linux/i2c.h>
#include <linux/i2c/twl.h>
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
-#include <plat/cpu.h>
-#endif
+#include "twl-core.h"
/*
* The TWL4030 "Triton 2" is one of a family of a multi-function "Power
@@ -116,8 +114,8 @@
#define twl_has_watchdog() false
#endif
-#if defined(CONFIG_MFD_TWL4030_AUDIO) || defined(CONFIG_MFD_TWL4030_AUDIO_MODULE) ||\
- defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE)
+#if defined(CONFIG_MFD_TWL4030_AUDIO) || \
+ defined(CONFIG_MFD_TWL4030_AUDIO_MODULE)
#define twl_has_codec() true
#else
#define twl_has_codec() false
@@ -147,12 +145,10 @@
#define SUB_CHIP_ID1 1
#define SUB_CHIP_ID2 2
#define SUB_CHIP_ID3 3
+#define SUB_CHIP_ID_INVAL 0xff
#define TWL_MODULE_LAST TWL4030_MODULE_LAST
-#define TWL4030_NR_IRQS 34 /* core:8, power:8, gpio: 18 */
-#define TWL6030_NR_IRQS 20
-
/* Base Address defns for twl4030_map[] */
/* subchip/slave 0 - USB ID */
@@ -314,7 +310,7 @@ static struct twl_mapping twl6030_map[] = {
* so they continue to match the order in this table.
*/
{ SUB_CHIP_ID1, TWL6030_BASEADD_USB },
- { SUB_CHIP_ID3, TWL6030_BASEADD_AUDIO },
+ { SUB_CHIP_ID_INVAL, TWL6030_BASEADD_AUDIO },
{ SUB_CHIP_ID2, TWL6030_BASEADD_DIEID },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID1, TWL6030_BASEADD_PIH },
@@ -376,6 +372,11 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
return -EPERM;
}
sid = twl_map[mod_no].sid;
+ if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
+ pr_err("%s: module %d is not part of the pmic\n",
+ DRIVER_NAME, mod_no);
+ return -EINVAL;
+ }
twl = &twl_modules[sid];
mutex_lock(&twl->xfer_lock);
@@ -433,6 +434,11 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
return -EPERM;
}
sid = twl_map[mod_no].sid;
+ if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
+ pr_err("%s: module %d is not part of the pmic\n",
+ DRIVER_NAME, mod_no);
+ return -EINVAL;
+ }
twl = &twl_modules[sid];
mutex_lock(&twl->xfer_lock);
@@ -663,7 +669,8 @@ add_regulator(int num, struct regulator_init_data *pdata,
*/
static int
-add_children(struct twl4030_platform_data *pdata, unsigned long features)
+add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
+ unsigned long features)
{
struct device *child;
unsigned sub_chip_id;
@@ -671,7 +678,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
if (twl_has_gpio() && pdata->gpio) {
child = add_child(SUB_CHIP_ID1, "twl4030_gpio",
pdata->gpio, sizeof(*pdata->gpio),
- false, pdata->irq_base + GPIO_INTR_OFFSET, 0);
+ false, irq_base + GPIO_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -679,7 +686,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
if (twl_has_keypad() && pdata->keypad) {
child = add_child(SUB_CHIP_ID2, "twl4030_keypad",
pdata->keypad, sizeof(*pdata->keypad),
- true, pdata->irq_base + KEYPAD_INTR_OFFSET, 0);
+ true, irq_base + KEYPAD_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -687,7 +694,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 + MADC_INTR_OFFSET, 0);
+ true, irq_base + MADC_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -703,7 +710,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
sub_chip_id = twl_map[TWL_MODULE_RTC].sid;
child = add_child(sub_chip_id, "twl_rtc",
NULL, 0,
- true, pdata->irq_base + RTC_INTR_OFFSET, 0);
+ true, irq_base + RTC_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -756,8 +763,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 + USB_PRES_INTR_OFFSET,
- pdata->irq_base + USB_INTR_OFFSET);
+ irq_base + USB_PRES_INTR_OFFSET,
+ irq_base + USB_INTR_OFFSET);
if (IS_ERR(child))
return PTR_ERR(child);
@@ -805,8 +812,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
pdata->usb, sizeof(*pdata->usb),
true,
/* irq1 = VBUS_PRES, irq0 = USB ID */
- pdata->irq_base + USBOTG_INTR_OFFSET,
- pdata->irq_base + USB_PRES_INTR_OFFSET);
+ irq_base + USBOTG_INTR_OFFSET,
+ irq_base + USB_PRES_INTR_OFFSET);
if (IS_ERR(child))
return PTR_ERR(child);
@@ -833,7 +840,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
if (twl_has_pwrbutton() && twl_class_is_4030()) {
child = add_child(1, "twl4030_pwrbutton",
- NULL, 0, true, pdata->irq_base + 8 + 0, 0);
+ NULL, 0, true, irq_base + 8 + 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -847,15 +854,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
- if (twl_has_codec() && pdata->audio && twl_class_is_6030()) {
- sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
- child = add_child(sub_chip_id, "twl6040",
- pdata->audio, sizeof(*pdata->audio),
- false, 0, 0);
- if (IS_ERR(child))
- return PTR_ERR(child);
- }
-
/* twl4030 regulators */
if (twl_has_regulator() && twl_class_is_4030()) {
child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1,
@@ -1092,8 +1090,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
child = add_child(3, "twl4030_bci",
pdata->bci, sizeof(*pdata->bci), false,
/* irq0 = CHG_PRES, irq1 = BCI */
- pdata->irq_base + BCI_PRES_INTR_OFFSET,
- pdata->irq_base + BCI_INTR_OFFSET);
+ irq_base + BCI_PRES_INTR_OFFSET,
+ irq_base + BCI_INTR_OFFSET);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -1193,26 +1191,24 @@ static void clocks_init(struct device *dev,
/*----------------------------------------------------------------------*/
-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 twl_remove(struct i2c_client *client)
{
- unsigned i;
+ unsigned i, num_slaves;
int status;
- if (twl_class_is_4030())
+ if (twl_class_is_4030()) {
status = twl4030_exit_irq();
- else
+ num_slaves = TWL_NUM_SLAVES;
+ } else {
status = twl6030_exit_irq();
+ num_slaves = TWL_NUM_SLAVES - 1;
+ }
if (status < 0)
return status;
- for (i = 0; i < TWL_NUM_SLAVES; i++) {
+ for (i = 0; i < num_slaves; i++) {
struct twl_client *twl = &twl_modules[i];
if (twl->client && twl->client != client)
@@ -1223,20 +1219,15 @@ static int twl_remove(struct i2c_client *client)
return 0;
}
-/* NOTE: this driver only handles a single twl4030/tps659x0 chip */
+/* NOTE: This driver only handles a single twl4030/tps659x0 chip */
static int __devinit
twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
- int status;
- unsigned i;
struct twl4030_platform_data *pdata = client->dev.platform_data;
struct device_node *node = client->dev.of_node;
- u8 temp;
- int ret = 0;
- int nr_irqs = TWL4030_NR_IRQS;
-
- if ((id->driver_data) & TWL6030_CLASS)
- nr_irqs = TWL6030_NR_IRQS;
+ int irq_base = 0;
+ int status;
+ unsigned i, num_slaves;
if (node && !pdata) {
/*
@@ -1255,17 +1246,6 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -EINVAL;
}
- status = irq_alloc_descs(-1, pdata->irq_base, nr_irqs, 0);
- if (IS_ERR_VALUE(status)) {
- dev_err(&client->dev, "Fail to allocate IRQ descs\n");
- return status;
- }
-
- pdata->irq_base = status;
- pdata->irq_end = pdata->irq_base + nr_irqs;
- irq_domain_add_legacy(node, nr_irqs, pdata->irq_base, 0,
- &irq_domain_simple_ops, NULL);
-
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
dev_dbg(&client->dev, "can't talk I2C?\n");
return -EIO;
@@ -1276,13 +1256,23 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -EBUSY;
}
- for (i = 0; i < TWL_NUM_SLAVES; i++) {
- struct twl_client *twl = &twl_modules[i];
+ if ((id->driver_data) & TWL6030_CLASS) {
+ twl_id = TWL6030_CLASS_ID;
+ twl_map = &twl6030_map[0];
+ num_slaves = TWL_NUM_SLAVES - 1;
+ } else {
+ twl_id = TWL4030_CLASS_ID;
+ twl_map = &twl4030_map[0];
+ num_slaves = TWL_NUM_SLAVES;
+ }
+
+ for (i = 0; i < num_slaves; i++) {
+ struct twl_client *twl = &twl_modules[i];
twl->address = client->addr + i;
- if (i == 0)
+ if (i == 0) {
twl->client = client;
- else {
+ } else {
twl->client = i2c_new_dummy(client->adapter,
twl->address);
if (!twl->client) {
@@ -1294,22 +1284,16 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
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, pdata->clock);
/* read TWL IDCODE Register */
if (twl_id == TWL4030_CLASS_ID) {
- ret = twl_read_idcode_register();
- WARN(ret < 0, "Error: reading twl_idcode register value\n");
+ status = twl_read_idcode_register();
+ WARN(status < 0, "Error: reading twl_idcode register value\n");
}
/* load power event scripts */
@@ -1317,31 +1301,31 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
twl4030_power_init(pdata->power);
/* Maybe init the T2 Interrupt subsystem */
- if (client->irq
- && pdata->irq_base
- && pdata->irq_end > pdata->irq_base) {
+ if (client->irq) {
if (twl_class_is_4030()) {
twl4030_init_chip_irq(id->name);
- status = twl4030_init_irq(client->irq, pdata->irq_base,
- pdata->irq_end);
+ irq_base = twl4030_init_irq(&client->dev, client->irq);
} else {
- status = twl6030_init_irq(client->irq, pdata->irq_base,
- pdata->irq_end);
+ irq_base = twl6030_init_irq(&client->dev, client->irq);
}
- if (status < 0)
+ if (irq_base < 0) {
+ status = irq_base;
goto fail;
+ }
}
- /* Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface.
+ /*
+ * Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface.
* Program I2C_SCL_CTRL_PU(bit 0)=0, I2C_SDA_CTRL_PU (bit 2)=0,
* SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0.
*/
-
if (twl_class_is_4030()) {
+ u8 temp;
+
twl_i2c_read_u8(TWL4030_MODULE_INTBR, &temp, REG_GPPUPDCTR1);
temp &= ~(SR_I2C_SDA_CTRL_PU | SR_I2C_SCL_CTRL_PU | \
- I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);
+ I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);
twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
}
@@ -1349,11 +1333,12 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (node)
status = of_platform_populate(node, NULL, NULL, &client->dev);
if (status)
- status = add_children(pdata, id->driver_data);
+ status = add_children(pdata, irq_base, id->driver_data);
fail:
if (status < 0)
twl_remove(client);
+
return status;
}
diff --git a/drivers/mfd/twl-core.h b/drivers/mfd/twl-core.h
index 8c50a55..6ff99dc 100644
--- a/drivers/mfd/twl-core.h
+++ b/drivers/mfd/twl-core.h
@@ -1,9 +1,9 @@
#ifndef __TWL_CORE_H__
#define __TWL_CORE_H__
-extern int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
+extern int twl6030_init_irq(struct device *dev, int irq_num);
extern int twl6030_exit_irq(void);
-extern int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
+extern int twl4030_init_irq(struct device *dev, int irq_num);
extern int twl4030_exit_irq(void);
extern int twl4030_init_chip_irq(const char *chip);
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index b69bb51..5d656e8 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -28,10 +28,12 @@
*/
#include <linux/init.h>
+#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
-
+#include <linux/of.h>
+#include <linux/irqdomain.h>
#include <linux/i2c/twl.h>
#include "twl-core.h"
@@ -53,13 +55,14 @@
* base + 8 .. base + 15 SIH for PWR_INT
* base + 16 .. base + 33 SIH for GPIO
*/
+#define TWL4030_CORE_NR_IRQS 8
+#define TWL4030_PWR_NR_IRQS 8
/* PIH register offsets */
#define REG_PIH_ISR_P1 0x01
#define REG_PIH_ISR_P2 0x02
#define REG_PIH_SIR 0x03 /* for testing */
-
/* Linux could (eventually) use either IRQ line */
static int irq_line;
@@ -111,7 +114,8 @@ static int nr_sih_modules;
#define TWL4030_MODULE_INT_PWR TWL4030_MODULE_INT
-/* Order in this table matches order in PIH_ISR. That is,
+/*
+ * Order in this table matches order in PIH_ISR. That is,
* BIT(n) in PIH_ISR is sih_modules[n].
*/
/* sih_modules_twl4030 is used both in twl4030 and twl5030 */
@@ -288,7 +292,6 @@ static unsigned twl4030_irq_base;
*/
static irqreturn_t handle_twl4030_pih(int irq, void *devid)
{
- int module_irq;
irqreturn_t ret;
u8 pih_isr;
@@ -299,16 +302,18 @@ static irqreturn_t handle_twl4030_pih(int irq, void *devid)
return IRQ_NONE;
}
- /* these handlers deal with the relevant SIH irq status */
- for (module_irq = twl4030_irq_base;
- pih_isr;
- pih_isr >>= 1, module_irq++) {
- if (pih_isr & 0x1)
- handle_nested_irq(module_irq);
+ while (pih_isr) {
+ unsigned long pending = __ffs(pih_isr);
+ unsigned int irq;
+
+ pih_isr &= ~BIT(pending);
+ irq = pending + twl4030_irq_base;
+ handle_nested_irq(irq);
}
return IRQ_HANDLED;
}
+
/*----------------------------------------------------------------------*/
/*
@@ -337,7 +342,6 @@ static int twl4030_init_sih_modules(unsigned line)
memset(buf, 0xff, sizeof buf);
sih = sih_modules;
for (i = 0; i < nr_sih_modules; i++, sih++) {
-
/* skip USB -- it's funky */
if (!sih->bytes_ixr)
continue;
@@ -352,7 +356,8 @@ static int twl4030_init_sih_modules(unsigned line)
pr_err("twl4030: err %d initializing %s %s\n",
status, sih->name, "IMR");
- /* Maybe disable "exclusive" mode; buffer second pending irq;
+ /*
+ * Maybe disable "exclusive" mode; buffer second pending irq;
* set Clear-On-Read (COR) bit.
*
* NOTE that sometimes COR polarity is documented as being
@@ -382,7 +387,8 @@ static int twl4030_init_sih_modules(unsigned line)
if (sih->irq_lines <= line)
continue;
- /* Clear pending interrupt status. Either the read was
+ /*
+ * 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.
@@ -398,7 +404,8 @@ static int twl4030_init_sih_modules(unsigned line)
status = twl_i2c_write(sih->module, buf,
sih->mask[line].isr_offset,
sih->bytes_ixr);
- /* else COR=1 means read sufficed.
+ /*
+ * else COR=1 means read sufficed.
* (for most SIH modules...)
*/
}
@@ -410,7 +417,8 @@ static int twl4030_init_sih_modules(unsigned line)
static inline void activate_irq(int irq)
{
#ifdef CONFIG_ARM
- /* ARM requires an extra step to clear IRQ_NOREQUEST, which it
+ /*
+ * 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);
@@ -620,33 +628,24 @@ static irqreturn_t handle_twl4030_sih(int irq, void *data)
return IRQ_HANDLED;
}
-static unsigned twl4030_irq_next;
-
-/* returns the first IRQ used by this SIH bank,
- * or negative errno
- */
-int twl4030_sih_setup(int module)
+/* returns the first IRQ used by this SIH bank, or negative errno */
+int twl4030_sih_setup(struct device *dev, int module, int irq_base)
{
int sih_mod;
const struct sih *sih = NULL;
struct sih_agent *agent;
int i, irq;
int status = -EINVAL;
- unsigned irq_base = twl4030_irq_next;
/* only support modules with standard clear-on-read for now */
- for (sih_mod = 0, sih = sih_modules;
- sih_mod < nr_sih_modules;
+ for (sih_mod = 0, sih = 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,
- "irq %d for %s too big\n",
- irq_base + sih->bits,
- sih->name))
- status = 0;
+ status = 0;
break;
}
}
+
if (status < 0)
return status;
@@ -654,8 +653,6 @@ int twl4030_sih_setup(int module)
if (!agent)
return -ENOMEM;
- status = 0;
-
agent->irq_base = irq_base;
agent->sih = sih;
agent->imr = ~0;
@@ -671,8 +668,6 @@ int twl4030_sih_setup(int module)
activate_irq(irq);
}
- twl4030_irq_next += i;
-
/* replace generic PIH handler (handle_simple_irq) */
irq = sih_mod + twl4030_irq_base;
irq_set_handler_data(irq, agent);
@@ -680,26 +675,43 @@ int twl4030_sih_setup(int module)
status = request_threaded_irq(irq, NULL, handle_twl4030_sih, 0,
agent->irq_name ?: sih->name, NULL);
- pr_info("twl4030: %s (irq %d) chaining IRQs %d..%d\n", sih->name,
- irq, irq_base, twl4030_irq_next - 1);
+ dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", sih->name,
+ irq, irq_base, irq_base + i - 1);
return status < 0 ? status : irq_base;
}
/* FIXME need a call to reverse twl4030_sih_setup() ... */
-
/*----------------------------------------------------------------------*/
/* FIXME pass in which interrupt line we'll use ... */
#define twl_irq_line 0
-int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
+int twl4030_init_irq(struct device *dev, int irq_num)
{
static struct irq_chip twl4030_irq_chip;
+ int status, i;
+ int irq_base, irq_end, nr_irqs;
+ struct device_node *node = dev->of_node;
- int status;
- int i;
+ /*
+ * TWL core and pwr interrupts must be contiguous because
+ * the hwirqs numbers are defined contiguously from 1 to 15.
+ * Create only one domain for both.
+ */
+ nr_irqs = TWL4030_PWR_NR_IRQS + TWL4030_CORE_NR_IRQS;
+
+ irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
+ if (IS_ERR_VALUE(irq_base)) {
+ dev_err(dev, "Fail to allocate IRQ descs\n");
+ return irq_base;
+ }
+
+ irq_domain_add_legacy(node, nr_irqs, irq_base, 0,
+ &irq_domain_simple_ops, NULL);
+
+ irq_end = irq_base + TWL4030_CORE_NR_IRQS;
/*
* Mask and clear all TWL4030 interrupts since initially we do
@@ -711,7 +723,8 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
twl4030_irq_base = irq_base;
- /* install an irq handler for each of the SIH modules;
+ /*
+ * Install an irq handler for each of the SIH modules;
* clone dummy irq_chip since PIH can't *do* anything
*/
twl4030_irq_chip = dummy_irq_chip;
@@ -725,14 +738,14 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
irq_set_nested_thread(i, 1);
activate_irq(i);
}
- twl4030_irq_next = i;
- pr_info("twl4030: %s (irq %d) chaining IRQs %d..%d\n", "PIH",
- irq_num, irq_base, twl4030_irq_next - 1);
+
+ dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", "PIH",
+ irq_num, irq_base, irq_end);
/* ... and the PWR_INT module ... */
- status = twl4030_sih_setup(TWL4030_MODULE_INT);
+ status = twl4030_sih_setup(dev, TWL4030_MODULE_INT, irq_end);
if (status < 0) {
- pr_err("twl4030: sih_setup PWR INT --> %d\n", status);
+ dev_err(dev, "sih_setup PWR INT --> %d\n", status);
goto fail;
}
@@ -741,11 +754,11 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
IRQF_ONESHOT,
"TWL4030-PIH", NULL);
if (status < 0) {
- pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status);
+ dev_err(dev, "could not claim irq%d: %d\n", irq_num, status);
goto fail_rqirq;
}
- return status;
+ return irq_base;
fail_rqirq:
/* clean up twl4030_sih_setup */
fail:
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index c6b456a..b76902f 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -39,6 +39,8 @@
#include <linux/i2c/twl.h>
#include <linux/platform_device.h>
#include <linux/suspend.h>
+#include <linux/of.h>
+#include <linux/irqdomain.h>
#include "twl-core.h"
@@ -51,8 +53,8 @@
*
* We set up IRQs starting at a platform-specified base. An interrupt map table,
* specifies mapping between interrupt number and the associated module.
- *
*/
+#define TWL6030_NR_IRQS 20
static int twl6030_interrupt_mapping[24] = {
PWR_INTR_OFFSET, /* Bit 0 PWRON */
@@ -185,8 +187,17 @@ static int twl6030_irq_thread(void *data)
}
local_irq_enable();
}
- ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes,
- REG_INT_STS_A, 3); /* clear INT_STS_A */
+
+ /*
+ * NOTE:
+ * Simulation confirms that documentation is wrong w.r.t the
+ * interrupt status clear operation. A single *byte* write to
+ * any one of STS_A to STS_C register results in all three
+ * STS registers being reset. Since it does not matter which
+ * value is written, all three registers are cleared on a
+ * single byte write, so we just use 0x0 to clear.
+ */
+ ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A);
if (ret)
pr_warning("twl6030: I2C error in clearing PIH ISR\n");
@@ -227,7 +238,7 @@ static inline void activate_irq(int irq)
#endif
}
-int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)
+static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)
{
if (on)
atomic_inc(&twl6030_wakeirqs);
@@ -237,11 +248,6 @@ int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)
return 0;
}
-/*----------------------------------------------------------------------*/
-
-static unsigned twl6030_irq_next;
-
-/*----------------------------------------------------------------------*/
int twl6030_interrupt_unmask(u8 bit_mask, u8 offset)
{
int ret;
@@ -311,7 +317,8 @@ int twl6030_mmc_card_detect_config(void)
ret);
return ret;
}
- return 0;
+
+ return twl6030_irq_base + MMCDETECT_INTR_OFFSET;
}
EXPORT_SYMBOL(twl6030_mmc_card_detect_config);
@@ -340,29 +347,44 @@ int twl6030_mmc_card_detect(struct device *dev, int slot)
}
EXPORT_SYMBOL(twl6030_mmc_card_detect);
-int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
+int twl6030_init_irq(struct device *dev, int irq_num)
{
-
- int status = 0;
- int i;
+ struct device_node *node = dev->of_node;
+ int nr_irqs, irq_base, irq_end;
struct task_struct *task;
- int ret;
- u8 mask[4];
+ static struct irq_chip twl6030_irq_chip;
+ int status = 0;
+ int i;
+ u8 mask[4];
+
+ nr_irqs = TWL6030_NR_IRQS;
+
+ irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
+ if (IS_ERR_VALUE(irq_base)) {
+ dev_err(dev, "Fail to allocate IRQ descs\n");
+ return irq_base;
+ }
+
+ irq_domain_add_legacy(node, nr_irqs, irq_base, 0,
+ &irq_domain_simple_ops, NULL);
+
+ irq_end = irq_base + nr_irqs;
- 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 */
+
+ /* mask all int lines */
+ twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_LINE_A, 3);
+ /* mask all int sts */
+ twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_STS_A, 3);
+ /* clear INT_STS_A,B,C */
+ twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_STS_A, 3);
twl6030_irq_base = irq_base;
- /* install an irq handler for each of the modules;
+ /*
+ * 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;
@@ -377,30 +399,29 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
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);
+ dev_info(dev, "PIH (irq %d) chaining IRQs %d..%d\n",
+ irq_num, irq_base, irq_end);
/* install an irq handler to demultiplex the TWL6030 interrupt */
init_completion(&irq_event);
- status = request_irq(irq_num, handle_twl6030_pih, 0,
- "TWL6030-PIH", &irq_event);
+ status = request_irq(irq_num, handle_twl6030_pih, 0, "TWL6030-PIH",
+ &irq_event);
if (status < 0) {
- pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
+ dev_err(dev, "could not claim irq %d: %d\n", irq_num, status);
goto fail_irq;
}
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);
+ dev_err(dev, "could not create irq %d thread!\n", irq_num);
status = PTR_ERR(task);
goto fail_kthread;
}
twl_irq = irq_num;
register_pm_notifier(&twl6030_irq_pm_notifier_block);
- return status;
+ return irq_base;
fail_kthread:
free_irq(irq_num, &irq_event);
@@ -408,6 +429,7 @@ fail_kthread:
fail_irq:
for (i = irq_base; i < irq_end; i++)
irq_set_chip_and_handler(i, NULL, NULL);
+
return status;
}
diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c
index cea9da6..b63c075 100644
--- a/drivers/mfd/ucb1x00-assabet.c
+++ b/drivers/mfd/ucb1x00-assabet.c
@@ -11,14 +11,15 @@
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
#include <linux/fs.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
#include <linux/proc_fs.h>
-#include <linux/device.h>
#include <linux/mfd/ucb1x00.h>
-#include <mach/dma.h>
-
-
#define UCB1X00_ATTR(name,input)\
static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \
char *buf) \
@@ -38,14 +39,45 @@ UCB1X00_ATTR(batt_temp, UCB_ADC_INP_AD2);
static int ucb1x00_assabet_add(struct ucb1x00_dev *dev)
{
- device_create_file(&dev->ucb->dev, &dev_attr_vbatt);
- device_create_file(&dev->ucb->dev, &dev_attr_vcharger);
- device_create_file(&dev->ucb->dev, &dev_attr_batt_temp);
+ struct ucb1x00 *ucb = dev->ucb;
+ struct platform_device *pdev;
+ struct gpio_keys_platform_data keys;
+ static struct gpio_keys_button buttons[6];
+ unsigned i;
+
+ memset(buttons, 0, sizeof(buttons));
+ memset(&keys, 0, sizeof(keys));
+
+ for (i = 0; i < ARRAY_SIZE(buttons); i++) {
+ buttons[i].code = BTN_0 + i;
+ buttons[i].gpio = ucb->gpio.base + i;
+ buttons[i].type = EV_KEY;
+ buttons[i].can_disable = true;
+ }
+
+ keys.buttons = buttons;
+ keys.nbuttons = ARRAY_SIZE(buttons);
+ keys.poll_interval = 50;
+ keys.name = "ucb1x00";
+
+ pdev = platform_device_register_data(&ucb->dev, "gpio-keys", -1,
+ &keys, sizeof(keys));
+
+ device_create_file(&ucb->dev, &dev_attr_vbatt);
+ device_create_file(&ucb->dev, &dev_attr_vcharger);
+ device_create_file(&ucb->dev, &dev_attr_batt_temp);
+
+ dev->priv = pdev;
return 0;
}
static void ucb1x00_assabet_remove(struct ucb1x00_dev *dev)
{
+ struct platform_device *pdev = dev->priv;
+
+ if (!IS_ERR(pdev))
+ platform_device_unregister(pdev);
+
device_remove_file(&dev->ucb->dev, &dev_attr_batt_temp);
device_remove_file(&dev->ucb->dev, &dev_attr_vcharger);
device_remove_file(&dev->ucb->dev, &dev_attr_vbatt);
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c
index febc90c..70f02da 100644
--- a/drivers/mfd/ucb1x00-core.c
+++ b/drivers/mfd/ucb1x00-core.c
@@ -23,14 +23,12 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/mfd/ucb1x00.h>
+#include <linux/pm.h>
#include <linux/gpio.h>
-#include <linux/semaphore.h>
-
-#include <mach/dma.h>
-#include <mach/hardware.h>
static DEFINE_MUTEX(ucb1x00_mutex);
static LIST_HEAD(ucb1x00_drivers);
@@ -102,7 +100,7 @@ void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear)
* ucb1x00_enable must have been called to enable the comms
* before using this function.
*
- * This function does not take any semaphores or spinlocks.
+ * This function does not take any mutexes or spinlocks.
*/
unsigned int ucb1x00_io_read(struct ucb1x00 *ucb)
{
@@ -120,14 +118,22 @@ static void ucb1x00_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
else
ucb->io_out &= ~(1 << offset);
+ ucb1x00_enable(ucb);
ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
+ ucb1x00_disable(ucb);
spin_unlock_irqrestore(&ucb->io_lock, flags);
}
static int ucb1x00_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
- return ucb1x00_reg_read(ucb, UCB_IO_DATA) & (1 << offset);
+ unsigned val;
+
+ ucb1x00_enable(ucb);
+ val = ucb1x00_reg_read(ucb, UCB_IO_DATA);
+ ucb1x00_disable(ucb);
+
+ return val & (1 << offset);
}
static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -137,7 +143,9 @@ static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
spin_lock_irqsave(&ucb->io_lock, flags);
ucb->io_dir &= ~(1 << offset);
+ ucb1x00_enable(ucb);
ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+ ucb1x00_disable(ucb);
spin_unlock_irqrestore(&ucb->io_lock, flags);
return 0;
@@ -157,6 +165,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
else
ucb->io_out &= ~mask;
+ ucb1x00_enable(ucb);
if (old != ucb->io_out)
ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
@@ -164,11 +173,19 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
ucb->io_dir |= mask;
ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
}
+ ucb1x00_disable(ucb);
spin_unlock_irqrestore(&ucb->io_lock, flags);
return 0;
}
+static int ucb1x00_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
+
+ return ucb->irq_base > 0 ? ucb->irq_base + offset : -ENXIO;
+}
+
/*
* UCB1300 data sheet says we must:
* 1. enable ADC => 5us (including reference startup time)
@@ -186,7 +203,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
* Any code wishing to use the ADC converter must call this
* function prior to using it.
*
- * This function takes the ADC semaphore to prevent two or more
+ * This function takes the ADC mutex to prevent two or more
* concurrent uses, and therefore may sleep. As a result, it
* can only be called from process context, not interrupt
* context.
@@ -196,7 +213,7 @@ static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
*/
void ucb1x00_adc_enable(struct ucb1x00 *ucb)
{
- down(&ucb->adc_sem);
+ mutex_lock(&ucb->adc_mutex);
ucb->adc_cr |= UCB_ADC_ENA;
@@ -218,7 +235,7 @@ void ucb1x00_adc_enable(struct ucb1x00 *ucb)
* complete (2 frames max without sync).
*
* If called for a synchronised ADC conversion, it may sleep
- * with the ADC semaphore held.
+ * with the ADC mutex held.
*/
unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync)
{
@@ -246,7 +263,7 @@ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync)
* ucb1x00_adc_disable - disable the ADC converter
* @ucb: UCB1x00 structure describing chip
*
- * Disable the ADC converter and release the ADC semaphore.
+ * Disable the ADC converter and release the ADC mutex.
*/
void ucb1x00_adc_disable(struct ucb1x00 *ucb)
{
@@ -254,7 +271,7 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb)
ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr);
ucb1x00_disable(ucb);
- up(&ucb->adc_sem);
+ mutex_unlock(&ucb->adc_mutex);
}
/*
@@ -265,10 +282,9 @@ void ucb1x00_adc_disable(struct ucb1x00 *ucb)
* SIBCLK to talk to the chip. We leave the clock running until
* we have finished processing all interrupts from the chip.
*/
-static irqreturn_t ucb1x00_irq(int irqnr, void *devid)
+static void ucb1x00_irq(unsigned int irq, struct irq_desc *desc)
{
- struct ucb1x00 *ucb = devid;
- struct ucb1x00_irq *irq;
+ struct ucb1x00 *ucb = irq_desc_get_handler_data(desc);
unsigned int isr, i;
ucb1x00_enable(ucb);
@@ -276,157 +292,104 @@ static irqreturn_t ucb1x00_irq(int irqnr, void *devid)
ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr);
ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
- for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++)
- if (isr & 1 && irq->fn)
- irq->fn(i, irq->devid);
+ for (i = 0; i < 16 && isr; i++, isr >>= 1, irq++)
+ if (isr & 1)
+ generic_handle_irq(ucb->irq_base + i);
ucb1x00_disable(ucb);
-
- return IRQ_HANDLED;
}
-/**
- * ucb1x00_hook_irq - hook a UCB1x00 interrupt
- * @ucb: UCB1x00 structure describing chip
- * @idx: interrupt index
- * @fn: function to call when interrupt is triggered
- * @devid: device id to pass to interrupt handler
- *
- * Hook the specified interrupt. You can only register one handler
- * for each interrupt source. The interrupt source is not enabled
- * by this function; use ucb1x00_enable_irq instead.
- *
- * Interrupt handlers will be called with other interrupts enabled.
- *
- * Returns zero on success, or one of the following errors:
- * -EINVAL if the interrupt index is invalid
- * -EBUSY if the interrupt has already been hooked
- */
-int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid)
+static void ucb1x00_irq_update(struct ucb1x00 *ucb, unsigned mask)
{
- struct ucb1x00_irq *irq;
- int ret = -EINVAL;
-
- if (idx < 16) {
- irq = ucb->irq_handler + idx;
- ret = -EBUSY;
-
- spin_lock_irq(&ucb->lock);
- if (irq->fn == NULL) {
- irq->devid = devid;
- irq->fn = fn;
- ret = 0;
- }
- spin_unlock_irq(&ucb->lock);
- }
- return ret;
+ ucb1x00_enable(ucb);
+ if (ucb->irq_ris_enbl & mask)
+ ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
+ ucb->irq_mask);
+ if (ucb->irq_fal_enbl & mask)
+ ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
+ ucb->irq_mask);
+ ucb1x00_disable(ucb);
}
-/**
- * ucb1x00_enable_irq - enable an UCB1x00 interrupt source
- * @ucb: UCB1x00 structure describing chip
- * @idx: interrupt index
- * @edges: interrupt edges to enable
- *
- * Enable the specified interrupt to trigger on %UCB_RISING,
- * %UCB_FALLING or both edges. The interrupt should have been
- * hooked by ucb1x00_hook_irq.
- */
-void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges)
+static void ucb1x00_irq_noop(struct irq_data *data)
{
- unsigned long flags;
+}
- if (idx < 16) {
- spin_lock_irqsave(&ucb->lock, flags);
+static void ucb1x00_irq_mask(struct irq_data *data)
+{
+ struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
+ unsigned mask = 1 << (data->irq - ucb->irq_base);
- ucb1x00_enable(ucb);
- if (edges & UCB_RISING) {
- ucb->irq_ris_enbl |= 1 << idx;
- ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
- }
- if (edges & UCB_FALLING) {
- ucb->irq_fal_enbl |= 1 << idx;
- ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
- }
- ucb1x00_disable(ucb);
- spin_unlock_irqrestore(&ucb->lock, flags);
- }
+ raw_spin_lock(&ucb->irq_lock);
+ ucb->irq_mask &= ~mask;
+ ucb1x00_irq_update(ucb, mask);
+ raw_spin_unlock(&ucb->irq_lock);
}
-/**
- * ucb1x00_disable_irq - disable an UCB1x00 interrupt source
- * @ucb: UCB1x00 structure describing chip
- * @edges: interrupt edges to disable
- *
- * Disable the specified interrupt triggering on the specified
- * (%UCB_RISING, %UCB_FALLING or both) edges.
- */
-void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges)
+static void ucb1x00_irq_unmask(struct irq_data *data)
{
- unsigned long flags;
+ struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
+ unsigned mask = 1 << (data->irq - ucb->irq_base);
- if (idx < 16) {
- spin_lock_irqsave(&ucb->lock, flags);
-
- ucb1x00_enable(ucb);
- if (edges & UCB_RISING) {
- ucb->irq_ris_enbl &= ~(1 << idx);
- ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
- }
- if (edges & UCB_FALLING) {
- ucb->irq_fal_enbl &= ~(1 << idx);
- ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
- }
- ucb1x00_disable(ucb);
- spin_unlock_irqrestore(&ucb->lock, flags);
- }
+ raw_spin_lock(&ucb->irq_lock);
+ ucb->irq_mask |= mask;
+ ucb1x00_irq_update(ucb, mask);
+ raw_spin_unlock(&ucb->irq_lock);
}
-/**
- * ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt
- * @ucb: UCB1x00 structure describing chip
- * @idx: interrupt index
- * @devid: device id.
- *
- * Disable the interrupt source and remove the handler. devid must
- * match the devid passed when hooking the interrupt.
- *
- * Returns zero on success, or one of the following errors:
- * -EINVAL if the interrupt index is invalid
- * -ENOENT if devid does not match
- */
-int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid)
+static int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type)
{
- struct ucb1x00_irq *irq;
- int ret;
+ struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
+ unsigned mask = 1 << (data->irq - ucb->irq_base);
- if (idx >= 16)
- goto bad;
+ raw_spin_lock(&ucb->irq_lock);
+ if (type & IRQ_TYPE_EDGE_RISING)
+ ucb->irq_ris_enbl |= mask;
+ else
+ ucb->irq_ris_enbl &= ~mask;
- irq = ucb->irq_handler + idx;
- ret = -ENOENT;
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ ucb->irq_fal_enbl |= mask;
+ else
+ ucb->irq_fal_enbl &= ~mask;
+ if (ucb->irq_mask & mask) {
+ ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
+ ucb->irq_mask);
+ ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
+ ucb->irq_mask);
+ }
+ raw_spin_unlock(&ucb->irq_lock);
- spin_lock_irq(&ucb->lock);
- if (irq->devid == devid) {
- ucb->irq_ris_enbl &= ~(1 << idx);
- ucb->irq_fal_enbl &= ~(1 << idx);
+ return 0;
+}
- ucb1x00_enable(ucb);
- ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
- ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
- ucb1x00_disable(ucb);
+static int ucb1x00_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+ struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
+ struct ucb1x00_plat_data *pdata = ucb->mcp->attached_device.platform_data;
+ unsigned mask = 1 << (data->irq - ucb->irq_base);
- irq->fn = NULL;
- irq->devid = NULL;
- ret = 0;
- }
- spin_unlock_irq(&ucb->lock);
- return ret;
+ if (!pdata || !pdata->can_wakeup)
+ return -EINVAL;
-bad:
- printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx);
- return -EINVAL;
+ raw_spin_lock(&ucb->irq_lock);
+ if (on)
+ ucb->irq_wake |= mask;
+ else
+ ucb->irq_wake &= ~mask;
+ raw_spin_unlock(&ucb->irq_lock);
+
+ return 0;
}
+static struct irq_chip ucb1x00_irqchip = {
+ .name = "ucb1x00",
+ .irq_ack = ucb1x00_irq_noop,
+ .irq_mask = ucb1x00_irq_mask,
+ .irq_unmask = ucb1x00_irq_unmask,
+ .irq_set_type = ucb1x00_irq_set_type,
+ .irq_set_wake = ucb1x00_irq_set_wake,
+};
+
static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv)
{
struct ucb1x00_dev *dev;
@@ -440,8 +403,8 @@ static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv)
ret = drv->add(dev);
if (ret == 0) {
- list_add(&dev->dev_node, &ucb->devs);
- list_add(&dev->drv_node, &drv->devs);
+ list_add_tail(&dev->dev_node, &ucb->devs);
+ list_add_tail(&dev->drv_node, &drv->devs);
} else {
kfree(dev);
}
@@ -533,98 +496,126 @@ static struct class ucb1x00_class = {
static int ucb1x00_probe(struct mcp *mcp)
{
- struct ucb1x00 *ucb;
+ struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data;
struct ucb1x00_driver *drv;
- unsigned int id;
+ struct ucb1x00 *ucb;
+ unsigned id, i, irq_base;
int ret = -ENODEV;
- int temp;
+
+ /* Tell the platform to deassert the UCB1x00 reset */
+ if (pdata && pdata->reset)
+ pdata->reset(UCB_RST_PROBE);
mcp_enable(mcp);
id = mcp_reg_read(mcp, UCB_ID);
+ mcp_disable(mcp);
if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143) {
printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);
- goto err_disable;
+ goto out;
}
ucb = kzalloc(sizeof(struct ucb1x00), GFP_KERNEL);
ret = -ENOMEM;
if (!ucb)
- goto err_disable;
-
+ goto out;
+ device_initialize(&ucb->dev);
ucb->dev.class = &ucb1x00_class;
ucb->dev.parent = &mcp->attached_device;
dev_set_name(&ucb->dev, "ucb1x00");
- spin_lock_init(&ucb->lock);
+ raw_spin_lock_init(&ucb->irq_lock);
spin_lock_init(&ucb->io_lock);
- sema_init(&ucb->adc_sem, 1);
+ mutex_init(&ucb->adc_mutex);
ucb->id = id;
ucb->mcp = mcp;
+
+ ret = device_add(&ucb->dev);
+ if (ret)
+ goto err_dev_add;
+
+ ucb1x00_enable(ucb);
ucb->irq = ucb1x00_detect_irq(ucb);
+ ucb1x00_disable(ucb);
if (ucb->irq == NO_IRQ) {
- printk(KERN_ERR "UCB1x00: IRQ probe failed\n");
+ dev_err(&ucb->dev, "IRQ probe failed\n");
ret = -ENODEV;
- goto err_free;
+ goto err_no_irq;
}
ucb->gpio.base = -1;
- if (mcp->gpio_base != 0) {
+ irq_base = pdata ? pdata->irq_base : 0;
+ ucb->irq_base = irq_alloc_descs(-1, irq_base, 16, -1);
+ if (ucb->irq_base < 0) {
+ dev_err(&ucb->dev, "unable to allocate 16 irqs: %d\n",
+ ucb->irq_base);
+ goto err_irq_alloc;
+ }
+
+ for (i = 0; i < 16; i++) {
+ unsigned irq = ucb->irq_base + i;
+
+ irq_set_chip_and_handler(irq, &ucb1x00_irqchip, handle_edge_irq);
+ irq_set_chip_data(irq, ucb);
+ set_irq_flags(irq, IRQF_VALID | IRQ_NOREQUEST);
+ }
+
+ irq_set_irq_type(ucb->irq, IRQ_TYPE_EDGE_RISING);
+ irq_set_handler_data(ucb->irq, ucb);
+ irq_set_chained_handler(ucb->irq, ucb1x00_irq);
+
+ if (pdata && pdata->gpio_base) {
ucb->gpio.label = dev_name(&ucb->dev);
- ucb->gpio.base = mcp->gpio_base;
+ ucb->gpio.dev = &ucb->dev;
+ ucb->gpio.owner = THIS_MODULE;
+ ucb->gpio.base = pdata->gpio_base;
ucb->gpio.ngpio = 10;
ucb->gpio.set = ucb1x00_gpio_set;
ucb->gpio.get = ucb1x00_gpio_get;
ucb->gpio.direction_input = ucb1x00_gpio_direction_input;
ucb->gpio.direction_output = ucb1x00_gpio_direction_output;
+ ucb->gpio.to_irq = ucb1x00_to_irq;
ret = gpiochip_add(&ucb->gpio);
if (ret)
- goto err_free;
+ goto err_gpio_add;
} else
dev_info(&ucb->dev, "gpio_base not set so no gpiolib support");
- ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING,
- "UCB1x00", ucb);
- if (ret) {
- printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n",
- ucb->irq, ret);
- goto err_gpio;
- }
-
mcp_set_drvdata(mcp, ucb);
- ret = device_register(&ucb->dev);
- if (ret)
- goto err_irq;
-
+ if (pdata)
+ device_set_wakeup_capable(&ucb->dev, pdata->can_wakeup);
INIT_LIST_HEAD(&ucb->devs);
mutex_lock(&ucb1x00_mutex);
- list_add(&ucb->node, &ucb1x00_devices);
+ list_add_tail(&ucb->node, &ucb1x00_devices);
list_for_each_entry(drv, &ucb1x00_drivers, node) {
ucb1x00_add_dev(ucb, drv);
}
mutex_unlock(&ucb1x00_mutex);
- goto out;
+ return ret;
- err_irq:
- free_irq(ucb->irq, ucb);
- err_gpio:
- if (ucb->gpio.base != -1)
- temp = gpiochip_remove(&ucb->gpio);
- err_free:
- kfree(ucb);
- err_disable:
- mcp_disable(mcp);
+ err_gpio_add:
+ irq_set_chained_handler(ucb->irq, NULL);
+ err_irq_alloc:
+ if (ucb->irq_base > 0)
+ irq_free_descs(ucb->irq_base, 16);
+ err_no_irq:
+ device_del(&ucb->dev);
+ err_dev_add:
+ put_device(&ucb->dev);
out:
+ if (pdata && pdata->reset)
+ pdata->reset(UCB_RST_PROBE_FAIL);
return ret;
}
static void ucb1x00_remove(struct mcp *mcp)
{
+ struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data;
struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
struct list_head *l, *n;
int ret;
@@ -643,8 +634,12 @@ static void ucb1x00_remove(struct mcp *mcp)
dev_err(&ucb->dev, "Can't remove gpio chip: %d\n", ret);
}
- free_irq(ucb->irq, ucb);
+ irq_set_chained_handler(ucb->irq, NULL);
+ irq_free_descs(ucb->irq_base, 16);
device_unregister(&ucb->dev);
+
+ if (pdata && pdata->reset)
+ pdata->reset(UCB_RST_REMOVE);
}
int ucb1x00_register_driver(struct ucb1x00_driver *drv)
@@ -653,7 +648,7 @@ int ucb1x00_register_driver(struct ucb1x00_driver *drv)
INIT_LIST_HEAD(&drv->devs);
mutex_lock(&ucb1x00_mutex);
- list_add(&drv->node, &ucb1x00_drivers);
+ list_add_tail(&drv->node, &ucb1x00_drivers);
list_for_each_entry(ucb, &ucb1x00_devices, node) {
ucb1x00_add_dev(ucb, drv);
}
@@ -674,44 +669,86 @@ void ucb1x00_unregister_driver(struct ucb1x00_driver *drv)
mutex_unlock(&ucb1x00_mutex);
}
-static int ucb1x00_suspend(struct mcp *mcp, pm_message_t state)
+static int ucb1x00_suspend(struct device *dev)
{
- struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
- struct ucb1x00_dev *dev;
+ struct ucb1x00_plat_data *pdata = dev->platform_data;
+ struct ucb1x00 *ucb = dev_get_drvdata(dev);
+ struct ucb1x00_dev *udev;
mutex_lock(&ucb1x00_mutex);
- list_for_each_entry(dev, &ucb->devs, dev_node) {
- if (dev->drv->suspend)
- dev->drv->suspend(dev, state);
+ list_for_each_entry(udev, &ucb->devs, dev_node) {
+ if (udev->drv->suspend)
+ udev->drv->suspend(udev);
}
mutex_unlock(&ucb1x00_mutex);
+
+ if (ucb->irq_wake) {
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&ucb->irq_lock, flags);
+ ucb1x00_enable(ucb);
+ ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
+ ucb->irq_wake);
+ ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
+ ucb->irq_wake);
+ ucb1x00_disable(ucb);
+ raw_spin_unlock_irqrestore(&ucb->irq_lock, flags);
+
+ enable_irq_wake(ucb->irq);
+ } else if (pdata && pdata->reset)
+ pdata->reset(UCB_RST_SUSPEND);
+
return 0;
}
-static int ucb1x00_resume(struct mcp *mcp)
+static int ucb1x00_resume(struct device *dev)
{
- struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
- struct ucb1x00_dev *dev;
+ struct ucb1x00_plat_data *pdata = dev->platform_data;
+ struct ucb1x00 *ucb = dev_get_drvdata(dev);
+ struct ucb1x00_dev *udev;
+
+ if (!ucb->irq_wake && pdata && pdata->reset)
+ pdata->reset(UCB_RST_RESUME);
+ ucb1x00_enable(ucb);
ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+
+ if (ucb->irq_wake) {
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&ucb->irq_lock, flags);
+ ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
+ ucb->irq_mask);
+ ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
+ ucb->irq_mask);
+ raw_spin_unlock_irqrestore(&ucb->irq_lock, flags);
+
+ disable_irq_wake(ucb->irq);
+ }
+ ucb1x00_disable(ucb);
+
mutex_lock(&ucb1x00_mutex);
- list_for_each_entry(dev, &ucb->devs, dev_node) {
- if (dev->drv->resume)
- dev->drv->resume(dev);
+ list_for_each_entry(udev, &ucb->devs, dev_node) {
+ if (udev->drv->resume)
+ udev->drv->resume(udev);
}
mutex_unlock(&ucb1x00_mutex);
return 0;
}
+static const struct dev_pm_ops ucb1x00_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ucb1x00_suspend, ucb1x00_resume)
+};
+
static struct mcp_driver ucb1x00_driver = {
.drv = {
.name = "ucb1x00",
+ .owner = THIS_MODULE,
+ .pm = &ucb1x00_pm_ops,
},
.probe = ucb1x00_probe,
.remove = ucb1x00_remove,
- .suspend = ucb1x00_suspend,
- .resume = ucb1x00_resume,
};
static int __init ucb1x00_init(void)
@@ -742,14 +779,10 @@ EXPORT_SYMBOL(ucb1x00_adc_enable);
EXPORT_SYMBOL(ucb1x00_adc_read);
EXPORT_SYMBOL(ucb1x00_adc_disable);
-EXPORT_SYMBOL(ucb1x00_hook_irq);
-EXPORT_SYMBOL(ucb1x00_free_irq);
-EXPORT_SYMBOL(ucb1x00_enable_irq);
-EXPORT_SYMBOL(ucb1x00_disable_irq);
-
EXPORT_SYMBOL(ucb1x00_register_driver);
EXPORT_SYMBOL(ucb1x00_unregister_driver);
+MODULE_ALIAS("mcp:ucb1x00");
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("UCB1x00 core driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c
index 63a3cbd..1e0e20c 100644
--- a/drivers/mfd/ucb1x00-ts.c
+++ b/drivers/mfd/ucb1x00-ts.c
@@ -20,8 +20,9 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
-#include <linux/smp.h>
+#include <linux/interrupt.h>
#include <linux/sched.h>
+#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/string.h>
@@ -32,7 +33,6 @@
#include <linux/kthread.h>
#include <linux/mfd/ucb1x00.h>
-#include <mach/dma.h>
#include <mach/collie.h>
#include <asm/mach-types.h>
@@ -42,6 +42,8 @@ struct ucb1x00_ts {
struct input_dev *idev;
struct ucb1x00 *ucb;
+ spinlock_t irq_lock;
+ unsigned irq_disabled;
wait_queue_head_t irq_wait;
struct task_struct *rtask;
u16 x_res;
@@ -238,7 +240,12 @@ static int ucb1x00_thread(void *_ts)
if (ucb1x00_ts_pen_down(ts)) {
set_current_state(TASK_INTERRUPTIBLE);
- ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, machine_is_collie() ? UCB_RISING : UCB_FALLING);
+ spin_lock_irq(&ts->irq_lock);
+ if (ts->irq_disabled) {
+ ts->irq_disabled = 0;
+ enable_irq(ts->ucb->irq_base + UCB_IRQ_TSPX);
+ }
+ spin_unlock_irq(&ts->irq_lock);
ucb1x00_disable(ts->ucb);
/*
@@ -281,23 +288,37 @@ static int ucb1x00_thread(void *_ts)
* We only detect touch screen _touches_ with this interrupt
* handler, and even then we just schedule our task.
*/
-static void ucb1x00_ts_irq(int idx, void *id)
+static irqreturn_t ucb1x00_ts_irq(int irq, void *id)
{
struct ucb1x00_ts *ts = id;
- ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
+ spin_lock(&ts->irq_lock);
+ ts->irq_disabled = 1;
+ disable_irq_nosync(ts->ucb->irq_base + UCB_IRQ_TSPX);
+ spin_unlock(&ts->irq_lock);
wake_up(&ts->irq_wait);
+
+ return IRQ_HANDLED;
}
static int ucb1x00_ts_open(struct input_dev *idev)
{
struct ucb1x00_ts *ts = input_get_drvdata(idev);
+ unsigned long flags = 0;
int ret = 0;
BUG_ON(ts->rtask);
+ if (machine_is_collie())
+ flags = IRQF_TRIGGER_RISING;
+ else
+ flags = IRQF_TRIGGER_FALLING;
+
+ ts->irq_disabled = 0;
+
init_waitqueue_head(&ts->irq_wait);
- ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
+ ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq,
+ flags, "ucb1x00-ts", ts);
if (ret < 0)
goto out;
@@ -314,7 +335,7 @@ static int ucb1x00_ts_open(struct input_dev *idev)
if (!IS_ERR(ts->rtask)) {
ret = 0;
} else {
- ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
+ free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
ts->rtask = NULL;
ret = -EFAULT;
}
@@ -334,7 +355,7 @@ static void ucb1x00_ts_close(struct input_dev *idev)
kthread_stop(ts->rtask);
ucb1x00_enable(ts->ucb);
- ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
+ free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
ucb1x00_disable(ts->ucb);
}
@@ -359,11 +380,13 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
ts->ucb = dev->ucb;
ts->idev = idev;
ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
+ spin_lock_init(&ts->irq_lock);
idev->name = "Touchscreen panel";
idev->id.product = ts->ucb->id;
idev->open = ucb1x00_ts_open;
idev->close = ucb1x00_ts_close;
+ idev->dev.parent = &ts->ucb->dev;
idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c
index 745c879..4bceee9 100644
--- a/drivers/mfd/wm831x-spi.c
+++ b/drivers/mfd/wm831x-spi.c
@@ -89,7 +89,7 @@ static const struct spi_device_id wm831x_spi_ids[] = {
{ "wm8326", WM8326 },
{ },
};
-MODULE_DEVICE_TABLE(spi, wm831x_spi_id);
+MODULE_DEVICE_TABLE(spi, wm831x_spi_ids);
static struct spi_driver wm831x_spi_driver = {
.driver = {
diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
index 237764a..1189a17 100644
--- a/drivers/mfd/wm8400-core.c
+++ b/drivers/mfd/wm8400-core.c
@@ -271,8 +271,7 @@ static int wm8400_init(struct wm8400 *wm8400,
return -EIO;
}
if (i != reg_data[WM8400_RESET_ID].default_val) {
- dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n",
- reg);
+ dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", i);
return -ENODEV;
}
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index 98733d4..9d7ca1e 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -639,7 +639,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
}
pm_runtime_enable(wm8994->dev);
- pm_runtime_resume(wm8994->dev);
+ pm_runtime_idle(wm8994->dev);
return 0;
diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c
index 7605b60..bfd25af 100644
--- a/drivers/mfd/wm8994-regmap.c
+++ b/drivers/mfd/wm8994-regmap.c
@@ -20,7 +20,6 @@
#include "wm8994.h"
static struct reg_default wm1811_defaults[] = {
- { 0x0000, 0x1811 }, /* R0 - Software Reset */
{ 0x0001, 0x0000 }, /* R1 - Power Management (1) */
{ 0x0002, 0x6000 }, /* R2 - Power Management (2) */
{ 0x0003, 0x0000 }, /* R3 - Power Management (3) */
@@ -61,7 +60,7 @@ static struct reg_default wm1811_defaults[] = {
{ 0x0036, 0x0000 }, /* R54 - Speaker Mixer */
{ 0x0037, 0x0000 }, /* R55 - Additional Control */
{ 0x0038, 0x0000 }, /* R56 - AntiPOP (1) */
- { 0x0039, 0x0180 }, /* R57 - AntiPOP (2) */
+ { 0x0039, 0x0000 }, /* R57 - AntiPOP (2) */
{ 0x003B, 0x000D }, /* R59 - LDO 1 */
{ 0x003C, 0x0003 }, /* R60 - LDO 2 */
{ 0x003D, 0x0039 }, /* R61 - MICBIAS1 */
@@ -69,16 +68,12 @@ static struct reg_default wm1811_defaults[] = {
{ 0x004C, 0x1F25 }, /* R76 - Charge Pump (1) */
{ 0x004D, 0xAB19 }, /* R77 - Charge Pump (2) */
{ 0x0051, 0x0004 }, /* R81 - Class W (1) */
- { 0x0054, 0x0000 }, /* R84 - DC Servo (1) */
{ 0x0055, 0x054A }, /* R85 - DC Servo (2) */
- { 0x0058, 0x0000 }, /* R88 - DC Servo Readback */
{ 0x0059, 0x0000 }, /* R89 - DC Servo (4) */
{ 0x0060, 0x0000 }, /* R96 - Analogue HP (1) */
{ 0x00C5, 0x0000 }, /* R197 - Class D Test (5) */
{ 0x00D0, 0x7600 }, /* R208 - Mic Detect 1 */
{ 0x00D1, 0x007F }, /* R209 - Mic Detect 2 */
- { 0x00D2, 0x0000 }, /* R210 - Mic Detect 3 */
- { 0x0100, 0x0100 }, /* R256 - Chip Revision */
{ 0x0101, 0x8004 }, /* R257 - Control Interface */
{ 0x0200, 0x0000 }, /* R512 - AIF1 Clocking (1) */
{ 0x0201, 0x0000 }, /* R513 - AIF1 Clocking (2) */
@@ -88,7 +83,6 @@ static struct reg_default wm1811_defaults[] = {
{ 0x0209, 0x0000 }, /* R521 - Clocking (2) */
{ 0x0210, 0x0083 }, /* R528 - AIF1 Rate */
{ 0x0211, 0x0083 }, /* R529 - AIF2 Rate */
- { 0x0212, 0x0000 }, /* R530 - Rate Status */
{ 0x0220, 0x0000 }, /* R544 - FLL1 Control (1) */
{ 0x0221, 0x0000 }, /* R545 - FLL1 Control (2) */
{ 0x0222, 0x0000 }, /* R546 - FLL1 Control (3) */
@@ -218,8 +212,6 @@ static struct reg_default wm1811_defaults[] = {
{ 0x070A, 0xA101 }, /* R1802 - GPIO 11 */
{ 0x0720, 0x0000 }, /* R1824 - Pull Control (1) */
{ 0x0721, 0x0156 }, /* R1825 - Pull Control (2) */
- { 0x0730, 0x0000 }, /* R1840 - Interrupt Status 1 */
- { 0x0731, 0x0000 }, /* R1841 - Interrupt Status 2 */
{ 0x0732, 0x0000 }, /* R1842 - Interrupt Raw Status 2 */
{ 0x0738, 0x07FF }, /* R1848 - Interrupt Status 1 Mask */
{ 0x0739, 0xDFEF }, /* R1849 - Interrupt Status 2 Mask */
@@ -228,7 +220,6 @@ static struct reg_default wm1811_defaults[] = {
};
static struct reg_default wm8994_defaults[] = {
- { 0x0000, 0x8994 }, /* R0 - Software Reset */
{ 0x0001, 0x0000 }, /* R1 - Power Management (1) */
{ 0x0002, 0x6000 }, /* R2 - Power Management (2) */
{ 0x0003, 0x0000 }, /* R3 - Power Management (3) */
@@ -275,12 +266,9 @@ static struct reg_default wm8994_defaults[] = {
{ 0x003C, 0x0003 }, /* R60 - LDO 2 */
{ 0x004C, 0x1F25 }, /* R76 - Charge Pump (1) */
{ 0x0051, 0x0004 }, /* R81 - Class W (1) */
- { 0x0054, 0x0000 }, /* R84 - DC Servo (1) */
{ 0x0055, 0x054A }, /* R85 - DC Servo (2) */
{ 0x0057, 0x0000 }, /* R87 - DC Servo (4) */
- { 0x0058, 0x0000 }, /* R88 - DC Servo Readback */
{ 0x0060, 0x0000 }, /* R96 - Analogue HP (1) */
- { 0x0100, 0x0003 }, /* R256 - Chip Revision */
{ 0x0101, 0x8004 }, /* R257 - Control Interface */
{ 0x0110, 0x0000 }, /* R272 - Write Sequencer Ctrl (1) */
{ 0x0111, 0x0000 }, /* R273 - Write Sequencer Ctrl (2) */
@@ -292,7 +280,6 @@ static struct reg_default wm8994_defaults[] = {
{ 0x0209, 0x0000 }, /* R521 - Clocking (2) */
{ 0x0210, 0x0083 }, /* R528 - AIF1 Rate */
{ 0x0211, 0x0083 }, /* R529 - AIF2 Rate */
- { 0x0212, 0x0000 }, /* R530 - Rate Status */
{ 0x0220, 0x0000 }, /* R544 - FLL1 Control (1) */
{ 0x0221, 0x0000 }, /* R545 - FLL1 Control (2) */
{ 0x0222, 0x0000 }, /* R546 - FLL1 Control (3) */
@@ -445,9 +432,6 @@ static struct reg_default wm8994_defaults[] = {
{ 0x070A, 0xA101 }, /* R1802 - GPIO 11 */
{ 0x0720, 0x0000 }, /* R1824 - Pull Control (1) */
{ 0x0721, 0x0156 }, /* R1825 - Pull Control (2) */
- { 0x0730, 0x0000 }, /* R1840 - Interrupt Status 1 */
- { 0x0731, 0x0000 }, /* R1841 - Interrupt Status 2 */
- { 0x0732, 0x0000 }, /* R1842 - Interrupt Raw Status 2 */
{ 0x0738, 0x07FF }, /* R1848 - Interrupt Status 1 Mask */
{ 0x0739, 0xFFFF }, /* R1849 - Interrupt Status 2 Mask */
{ 0x0740, 0x0000 }, /* R1856 - Interrupt Control */
@@ -455,7 +439,6 @@ static struct reg_default wm8994_defaults[] = {
};
static struct reg_default wm8958_defaults[] = {
- { 0x0000, 0x8958 }, /* R0 - Software Reset */
{ 0x0001, 0x0000 }, /* R1 - Power Management (1) */
{ 0x0002, 0x6000 }, /* R2 - Power Management (2) */
{ 0x0003, 0x0000 }, /* R3 - Power Management (3) */
@@ -970,6 +953,7 @@ static bool wm8994_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case WM8994_DC_SERVO_READBACK:
+ case WM8994_MICBIAS:
case WM8994_WRITE_SEQUENCER_CTRL_1:
case WM8994_WRITE_SEQUENCER_CTRL_2:
case WM8994_AIF1_ADC2_LEFT_VOLUME:
diff --git a/drivers/misc/sgi-gru/gru_instructions.h b/drivers/misc/sgi-gru/gru_instructions.h
index d95587c..04d5170 100644
--- a/drivers/misc/sgi-gru/gru_instructions.h
+++ b/drivers/misc/sgi-gru/gru_instructions.h
@@ -40,6 +40,7 @@ extern void gru_wait_abort_proc(void *cb);
*((volatile unsigned long *)(p)) = v; /* force st.rel */ \
} while (0)
#elif defined(CONFIG_X86_64)
+#include <asm/cacheflush.h>
#define __flush_cache(p) clflush(p)
#define gru_ordered_store_ulong(p, v) \
do { \
diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h
index 851b2f2..c862cd4 100644
--- a/drivers/misc/sgi-xp/xp.h
+++ b/drivers/misc/sgi-xp/xp.h
@@ -25,7 +25,6 @@
#endif
#if defined CONFIG_IA64
-#include <asm/system.h>
#include <asm/sn/arch.h> /* defines is_shub1() and is_shub2() */
#define is_shub() ia64_platform_is("sn2")
#endif
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index e5a3c7b..eed213a 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -41,7 +41,6 @@
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include "queue.h"
@@ -1080,6 +1079,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
struct mmc_blk_request *brq = &mqrq->brq;
struct request *req = mqrq->req;
struct mmc_blk_data *md = mq->data;
+ bool do_data_tag;
/*
* Reliable writes are used to implement Forced Unit Access and
@@ -1156,6 +1156,16 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
mmc_apply_rel_rw(brq, card, req);
/*
+ * Data tag is used only during writing meta data to speed
+ * up write and any subsequent read of this meta data
+ */
+ do_data_tag = (card->ext_csd.data_tag_unit_size) &&
+ (req->cmd_flags & REQ_META) &&
+ (rq_data_dir(req) == WRITE) &&
+ ((brq->data.blocks * brq->data.blksz) >=
+ card->ext_csd.data_tag_unit_size);
+
+ /*
* Pre-defined multi-block transfers are preferable to
* open ended-ones (and necessary for reliable writes).
* However, it is not sufficient to just send CMD23,
@@ -1173,13 +1183,13 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
* We'll avoid using CMD23-bounded multiblock writes for
* these, while retaining features like reliable writes.
*/
-
- if ((md->flags & MMC_BLK_CMD23) &&
- mmc_op_multi(brq->cmd.opcode) &&
- (do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23))) {
+ if ((md->flags & MMC_BLK_CMD23) && mmc_op_multi(brq->cmd.opcode) &&
+ (do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23) ||
+ do_data_tag)) {
brq->sbc.opcode = MMC_SET_BLOCK_COUNT;
brq->sbc.arg = brq->data.blocks |
- (do_rel_wr ? (1 << 31) : 0);
+ (do_rel_wr ? (1 << 31) : 0) |
+ (do_data_tag ? (1 << 29) : 0);
brq->sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
brq->mrq.sbc = &brq->sbc;
}
diff --git a/drivers/mmc/core/cd-gpio.c b/drivers/mmc/core/cd-gpio.c
index 082202a..29de31e 100644
--- a/drivers/mmc/core/cd-gpio.c
+++ b/drivers/mmc/core/cd-gpio.c
@@ -28,13 +28,17 @@ static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio,
- unsigned int irq, unsigned long flags)
+int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio)
{
size_t len = strlen(dev_name(host->parent)) + 4;
- struct mmc_cd_gpio *cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL);
+ struct mmc_cd_gpio *cd;
+ int irq = gpio_to_irq(gpio);
int ret;
+ if (irq < 0)
+ return irq;
+
+ cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL);
if (!cd)
return -ENOMEM;
@@ -45,7 +49,8 @@ int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio,
goto egpioreq;
ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt,
- flags, cd->label, host);
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ cd->label, host);
if (ret < 0)
goto eirqreq;
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 132378b..14f262e 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -188,6 +188,12 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
struct scatterlist *sg;
#endif
+ if (mrq->sbc) {
+ pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n",
+ mmc_hostname(host), mrq->sbc->opcode,
+ mrq->sbc->arg, mrq->sbc->flags);
+ }
+
pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
mmc_hostname(host), mrq->cmd->opcode,
mrq->cmd->arg, mrq->cmd->flags);
@@ -243,16 +249,17 @@ static void mmc_wait_done(struct mmc_request *mrq)
complete(&mrq->completion);
}
-static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
+static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
init_completion(&mrq->completion);
mrq->done = mmc_wait_done;
if (mmc_card_removed(host->card)) {
mrq->cmd->error = -ENOMEDIUM;
complete(&mrq->completion);
- return;
+ return -ENOMEDIUM;
}
mmc_start_request(host, mrq);
+ return 0;
}
static void mmc_wait_for_req_done(struct mmc_host *host,
@@ -336,6 +343,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
struct mmc_async_req *areq, int *error)
{
int err = 0;
+ int start_err = 0;
struct mmc_async_req *data = host->areq;
/* Prepare a new request */
@@ -345,30 +353,23 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
if (host->areq) {
mmc_wait_for_req_done(host, host->areq->mrq);
err = host->areq->err_check(host->card, host->areq);
- if (err) {
- /* post process the completed failed request */
- mmc_post_req(host, host->areq->mrq, 0);
- if (areq)
- /*
- * Cancel the new prepared request, because
- * it can't run until the failed
- * request has been properly handled.
- */
- mmc_post_req(host, areq->mrq, -EINVAL);
-
- host->areq = NULL;
- goto out;
- }
}
- if (areq)
- __mmc_start_req(host, areq->mrq);
+ if (!err && areq)
+ start_err = __mmc_start_req(host, areq->mrq);
if (host->areq)
mmc_post_req(host, host->areq->mrq, 0);
- host->areq = areq;
- out:
+ /* Cancel a prepared request if it was not started. */
+ if ((err || start_err) && areq)
+ mmc_post_req(host, areq->mrq, -EINVAL);
+
+ if (err)
+ host->areq = NULL;
+ else
+ host->areq = areq;
+
if (error)
*error = err;
return data;
@@ -599,105 +600,6 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
EXPORT_SYMBOL(mmc_align_data_size);
/**
- * mmc_host_enable - enable a host.
- * @host: mmc host to enable
- *
- * Hosts that support power saving can use the 'enable' and 'disable'
- * methods to exit and enter power saving states. For more information
- * see comments for struct mmc_host_ops.
- */
-int mmc_host_enable(struct mmc_host *host)
-{
- if (!(host->caps & MMC_CAP_DISABLE))
- return 0;
-
- if (host->en_dis_recurs)
- return 0;
-
- if (host->nesting_cnt++)
- return 0;
-
- cancel_delayed_work_sync(&host->disable);
-
- if (host->enabled)
- return 0;
-
- if (host->ops->enable) {
- int err;
-
- host->en_dis_recurs = 1;
- mmc_host_clk_hold(host);
- err = host->ops->enable(host);
- mmc_host_clk_release(host);
- host->en_dis_recurs = 0;
-
- if (err) {
- pr_debug("%s: enable error %d\n",
- mmc_hostname(host), err);
- return err;
- }
- }
- host->enabled = 1;
- return 0;
-}
-EXPORT_SYMBOL(mmc_host_enable);
-
-static int mmc_host_do_disable(struct mmc_host *host, int lazy)
-{
- if (host->ops->disable) {
- int err;
-
- host->en_dis_recurs = 1;
- mmc_host_clk_hold(host);
- err = host->ops->disable(host, lazy);
- mmc_host_clk_release(host);
- host->en_dis_recurs = 0;
-
- if (err < 0) {
- pr_debug("%s: disable error %d\n",
- mmc_hostname(host), err);
- return err;
- }
- if (err > 0) {
- unsigned long delay = msecs_to_jiffies(err);
-
- mmc_schedule_delayed_work(&host->disable, delay);
- }
- }
- host->enabled = 0;
- return 0;
-}
-
-/**
- * mmc_host_disable - disable a host.
- * @host: mmc host to disable
- *
- * Hosts that support power saving can use the 'enable' and 'disable'
- * methods to exit and enter power saving states. For more information
- * see comments for struct mmc_host_ops.
- */
-int mmc_host_disable(struct mmc_host *host)
-{
- int err;
-
- if (!(host->caps & MMC_CAP_DISABLE))
- return 0;
-
- if (host->en_dis_recurs)
- return 0;
-
- if (--host->nesting_cnt)
- return 0;
-
- if (!host->enabled)
- return 0;
-
- err = mmc_host_do_disable(host, 0);
- return err;
-}
-EXPORT_SYMBOL(mmc_host_disable);
-
-/**
* __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
* @abort: whether or not the operation should be aborted
@@ -735,8 +637,8 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
wake_up(&host->wq);
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait);
- if (!stop)
- mmc_host_enable(host);
+ if (host->ops->enable && !stop && host->claim_cnt == 1)
+ host->ops->enable(host);
return stop;
}
@@ -761,21 +663,28 @@ int mmc_try_claim_host(struct mmc_host *host)
claimed_host = 1;
}
spin_unlock_irqrestore(&host->lock, flags);
+ if (host->ops->enable && claimed_host && host->claim_cnt == 1)
+ host->ops->enable(host);
return claimed_host;
}
EXPORT_SYMBOL(mmc_try_claim_host);
/**
- * mmc_do_release_host - release a claimed host
+ * mmc_release_host - release a host
* @host: mmc host to release
*
- * If you successfully claimed a host, this function will
- * release it again.
+ * Release a MMC host, allowing others to claim the host
+ * for their operations.
*/
-void mmc_do_release_host(struct mmc_host *host)
+void mmc_release_host(struct mmc_host *host)
{
unsigned long flags;
+ WARN_ON(!host->claimed);
+
+ if (host->ops->disable && host->claim_cnt == 1)
+ host->ops->disable(host);
+
spin_lock_irqsave(&host->lock, flags);
if (--host->claim_cnt) {
/* Release for nested claim */
@@ -787,67 +696,6 @@ void mmc_do_release_host(struct mmc_host *host)
wake_up(&host->wq);
}
}
-EXPORT_SYMBOL(mmc_do_release_host);
-
-void mmc_host_deeper_disable(struct work_struct *work)
-{
- struct mmc_host *host =
- container_of(work, struct mmc_host, disable.work);
-
- /* If the host is claimed then we do not want to disable it anymore */
- if (!mmc_try_claim_host(host))
- return;
- mmc_host_do_disable(host, 1);
- mmc_do_release_host(host);
-}
-
-/**
- * mmc_host_lazy_disable - lazily disable a host.
- * @host: mmc host to disable
- *
- * Hosts that support power saving can use the 'enable' and 'disable'
- * methods to exit and enter power saving states. For more information
- * see comments for struct mmc_host_ops.
- */
-int mmc_host_lazy_disable(struct mmc_host *host)
-{
- if (!(host->caps & MMC_CAP_DISABLE))
- return 0;
-
- if (host->en_dis_recurs)
- return 0;
-
- if (--host->nesting_cnt)
- return 0;
-
- if (!host->enabled)
- return 0;
-
- if (host->disable_delay) {
- mmc_schedule_delayed_work(&host->disable,
- msecs_to_jiffies(host->disable_delay));
- return 0;
- } else
- return mmc_host_do_disable(host, 1);
-}
-EXPORT_SYMBOL(mmc_host_lazy_disable);
-
-/**
- * mmc_release_host - release a host
- * @host: mmc host to release
- *
- * Release a MMC host, allowing others to claim the host
- * for their operations.
- */
-void mmc_release_host(struct mmc_host *host)
-{
- WARN_ON(!host->claimed);
-
- mmc_host_lazy_disable(host);
-
- mmc_do_release_host(host);
-}
-
EXPORT_SYMBOL(mmc_release_host);
/*
@@ -2115,18 +1963,36 @@ int _mmc_detect_card_removed(struct mmc_host *host)
int mmc_detect_card_removed(struct mmc_host *host)
{
struct mmc_card *card = host->card;
+ int ret;
WARN_ON(!host->claimed);
+
+ if (!card)
+ return 1;
+
+ ret = mmc_card_removed(card);
/*
* The card will be considered unchanged unless we have been asked to
* detect a change or host requires polling to provide card detection.
*/
- if (card && !host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL))
- return mmc_card_removed(card);
+ if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL) &&
+ !(host->caps2 & MMC_CAP2_DETECT_ON_ERR))
+ return ret;
host->detect_change = 0;
+ if (!ret) {
+ ret = _mmc_detect_card_removed(host);
+ if (ret && (host->caps2 & MMC_CAP2_DETECT_ON_ERR)) {
+ /*
+ * Schedule a detect work as soon as possible to let a
+ * rescan handle the card removal.
+ */
+ cancel_delayed_work(&host->detect);
+ mmc_detect_change(host, 0);
+ }
+ }
- return _mmc_detect_card_removed(host);
+ return ret;
}
EXPORT_SYMBOL(mmc_detect_card_removed);
@@ -2203,8 +2069,6 @@ void mmc_stop_host(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags);
#endif
- if (host->caps & MMC_CAP_DISABLE)
- cancel_delayed_work(&host->disable);
cancel_delayed_work_sync(&host->detect);
mmc_flush_scheduled_work();
@@ -2399,13 +2263,11 @@ int mmc_suspend_host(struct mmc_host *host)
{
int err = 0;
- if (host->caps & MMC_CAP_DISABLE)
- cancel_delayed_work(&host->disable);
cancel_delayed_work(&host->detect);
mmc_flush_scheduled_work();
if (mmc_try_claim_host(host)) {
err = mmc_cache_ctrl(host, 0);
- mmc_do_release_host(host);
+ mmc_release_host(host);
} else {
err = -EBUSY;
}
@@ -2426,7 +2288,7 @@ int mmc_suspend_host(struct mmc_host *host)
if (host->bus_ops->suspend) {
err = host->bus_ops->suspend(host);
}
- mmc_do_release_host(host);
+ mmc_release_host(host);
if (err == -ENOSYS || !host->bus_ops->resume) {
/*
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index c3704e2..91c84c7 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -330,7 +330,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
- INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
#ifdef CONFIG_PM
host->pm_notify.notifier_call = mmc_pm_notify;
#endif
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index 08a7852..f2ab9e5 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -14,7 +14,6 @@
int mmc_register_host_class(void);
void mmc_unregister_host_class(void);
-void mmc_host_deeper_disable(struct work_struct *work);
#endif
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 2b9ed14..02914d6 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -519,6 +519,20 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
ext_csd[EXT_CSD_CACHE_SIZE + 1] << 8 |
ext_csd[EXT_CSD_CACHE_SIZE + 2] << 16 |
ext_csd[EXT_CSD_CACHE_SIZE + 3] << 24;
+
+ if (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 1)
+ card->ext_csd.data_sector_size = 4096;
+ else
+ card->ext_csd.data_sector_size = 512;
+
+ if ((ext_csd[EXT_CSD_DATA_TAG_SUPPORT] & 1) &&
+ (ext_csd[EXT_CSD_TAG_UNIT_SIZE] <= 8)) {
+ card->ext_csd.data_tag_unit_size =
+ ((unsigned int) 1 << ext_csd[EXT_CSD_TAG_UNIT_SIZE]) *
+ (card->ext_csd.data_sector_size);
+ } else {
+ card->ext_csd.data_tag_unit_size = 0;
+ }
}
out:
@@ -938,7 +952,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* If enhanced_area_en is TRUE, host needs to enable ERASE_GRP_DEF
* bit. This bit will be lost every time after a reset or power off.
*/
- if (card->ext_csd.enhanced_area_en) {
+ if (card->ext_csd.enhanced_area_en ||
+ (card->ext_csd.rev >= 3 && (host->caps2 & MMC_CAP2_HC_ERASE_SZ))) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_ERASE_GROUP_DEF, 1,
card->ext_csd.generic_cmd6_time);
@@ -1033,22 +1048,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
/*
- * Enable HPI feature (if supported)
- */
- if (card->ext_csd.hpi) {
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HPI_MGMT, 1, 0);
- if (err && err != -EBADMSG)
- goto free_card;
- if (err) {
- pr_warning("%s: Enabling HPI failed\n",
- mmc_hostname(card->host));
- err = 0;
- } else
- card->ext_csd.hpi_en = 1;
- }
-
- /*
* Compute bus speed.
*/
max_dtr = (unsigned int)-1;
@@ -1097,9 +1096,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* 4. execute tuning for HS200
*/
if ((host->caps2 & MMC_CAP2_HS200) &&
- card->host->ops->execute_tuning)
+ card->host->ops->execute_tuning) {
+ mmc_host_clk_hold(card->host);
err = card->host->ops->execute_tuning(card->host,
MMC_SEND_TUNING_BLOCK_HS200);
+ mmc_host_clk_release(card->host);
+ }
if (err) {
pr_warning("%s: tuning execution failed\n",
mmc_hostname(card->host));
@@ -1219,6 +1221,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
/*
+ * Enable HPI feature (if supported)
+ */
+ if (card->ext_csd.hpi) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HPI_MGMT, 1,
+ card->ext_csd.generic_cmd6_time);
+ if (err && err != -EBADMSG)
+ goto free_card;
+ if (err) {
+ pr_warning("%s: Enabling HPI failed\n",
+ mmc_hostname(card->host));
+ err = 0;
+ } else
+ card->ext_csd.hpi_en = 1;
+ }
+
+ /*
* If cache size is higher than 0, this indicates
* the existence of cache and it can be turned on.
*/
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 4d41fa9..69370f4 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -553,18 +553,22 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
{
struct mmc_command cmd = {0};
unsigned int opcode;
- unsigned int flags;
int err;
+ if (!card->ext_csd.hpi) {
+ pr_warning("%s: Card didn't support HPI command\n",
+ mmc_hostname(card->host));
+ return -EINVAL;
+ }
+
opcode = card->ext_csd.hpi_cmd;
if (opcode == MMC_STOP_TRANSMISSION)
- flags = MMC_RSP_R1 | MMC_CMD_AC;
+ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
else if (opcode == MMC_SEND_STATUS)
- flags = MMC_RSP_R1 | MMC_CMD_AC;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
cmd.opcode = opcode;
cmd.arg = card->rca << 16 | 1;
- cmd.flags = flags;
cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time;
err = mmc_wait_for_cmd(card->host, &cmd, 0);
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index ecbee9b..2bc06e7 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -533,6 +533,31 @@ config MMC_DW_IDMAC
Designware Mobile Storage IP block. This disables the external DMA
interface.
+config MMC_DW_PLTFM
+ tristate "Synopsys Designware MCI Support as platform device"
+ depends on MMC_DW
+ default y
+ help
+ This selects the common helper functions support for Host Controller
+ Interface based platform driver. Please select this option if the IP
+ is present as a platform device. This is the common interface for the
+ Synopsys Designware IP.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say Y.
+
+config MMC_DW_PCI
+ tristate "Synopsys Designware MCI support on PCI bus"
+ depends on MMC_DW && PCI
+ help
+ This selects the PCI bus for the Synopsys Designware Mobile Storage IP.
+ Select this option if the IP is present on PCI platform.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support"
depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 745f8fc..3e7e26d 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -39,6 +39,8 @@ obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_DW) += dw_mmc.o
+obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
+obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index e4449a5..9819dc0 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -24,6 +24,7 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/stat.h>
+#include <linux/types.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio.h>
@@ -173,6 +174,7 @@ struct atmel_mci {
struct atmel_mci_dma dma;
struct dma_chan *data_chan;
+ struct dma_slave_config dma_conf;
u32 cmd_status;
u32 data_status;
@@ -863,16 +865,17 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
if (data->flags & MMC_DATA_READ) {
direction = DMA_FROM_DEVICE;
- slave_dirn = DMA_DEV_TO_MEM;
+ host->dma_conf.direction = slave_dirn = DMA_DEV_TO_MEM;
} else {
direction = DMA_TO_DEVICE;
- slave_dirn = DMA_MEM_TO_DEV;
+ host->dma_conf.direction = slave_dirn = DMA_MEM_TO_DEV;
}
sglen = dma_map_sg(chan->device->dev, data->sg,
data->sg_len, direction);
- desc = chan->device->device_prep_slave_sg(chan,
+ dmaengine_slave_config(chan, &host->dma_conf);
+ desc = dmaengine_prep_slave_sg(chan,
data->sg, sglen, slave_dirn,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc)
@@ -1960,10 +1963,6 @@ static bool atmci_configure_dma(struct atmel_mci *host)
if (pdata && find_slave_dev(pdata->dma_slave)) {
dma_cap_mask_t mask;
- setup_dma_addr(pdata->dma_slave,
- host->mapbase + ATMCI_TDR,
- host->mapbase + ATMCI_RDR);
-
/* Try to grab a DMA channel */
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
@@ -1975,8 +1974,16 @@ static bool atmci_configure_dma(struct atmel_mci *host)
return false;
} else {
dev_info(&host->pdev->dev,
- "Using %s for DMA transfers\n",
+ "using %s for DMA transfers\n",
dma_chan_name(host->dma.chan));
+
+ host->dma_conf.src_addr = host->mapbase + ATMCI_RDR;
+ host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ host->dma_conf.src_maxburst = 1;
+ host->dma_conf.dst_addr = host->mapbase + ATMCI_TDR;
+ host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ host->dma_conf.dst_maxburst = 1;
+ host->dma_conf.device_fc = false;
return true;
}
}
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 64a8325..c1f3673 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -160,6 +160,16 @@ module_param(rw_threshold, uint, S_IRUGO);
MODULE_PARM_DESC(rw_threshold,
"Read/Write threshold. Default = 32");
+static unsigned poll_threshold = 128;
+module_param(poll_threshold, uint, S_IRUGO);
+MODULE_PARM_DESC(poll_threshold,
+ "Polling transaction size threshold. Default = 128");
+
+static unsigned poll_loopcount = 32;
+module_param(poll_loopcount, uint, S_IRUGO);
+MODULE_PARM_DESC(poll_loopcount,
+ "Maximum polling loop count. 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");
@@ -193,6 +203,7 @@ struct mmc_davinci_host {
bool use_dma;
bool do_dma;
bool sdio_int;
+ bool active_request;
/* Scatterlist DMA uses one or more parameter RAM entries:
* the main one (associated with rxdma or txdma) plus zero or
@@ -219,6 +230,7 @@ struct mmc_davinci_host {
#endif
};
+static irqreturn_t mmc_davinci_irq(int irq, void *dev_id);
/* PIO only */
static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host)
@@ -376,7 +388,20 @@ static void mmc_davinci_start_command(struct mmc_davinci_host *host,
writel(cmd->arg, host->base + DAVINCI_MMCARGHL);
writel(cmd_reg, host->base + DAVINCI_MMCCMD);
- writel(im_val, host->base + DAVINCI_MMCIM);
+
+ host->active_request = true;
+
+ if (!host->do_dma && host->bytes_left <= poll_threshold) {
+ u32 count = poll_loopcount;
+
+ while (host->active_request && count--) {
+ mmc_davinci_irq(0, host);
+ cpu_relax();
+ }
+ }
+
+ if (host->active_request)
+ writel(im_val, host->base + DAVINCI_MMCIM);
}
/*----------------------------------------------------------------------*/
@@ -915,6 +940,7 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data)
if (!data->stop || (host->cmd && host->cmd->error)) {
mmc_request_done(host->mmc, data->mrq);
writel(0, host->base + DAVINCI_MMCIM);
+ host->active_request = false;
} else
mmc_davinci_start_command(host, data->stop);
}
@@ -942,6 +968,7 @@ static void mmc_davinci_cmd_done(struct mmc_davinci_host *host,
cmd->mrq->cmd->retries = 0;
mmc_request_done(host->mmc, cmd->mrq);
writel(0, host->base + DAVINCI_MMCIM);
+ host->active_request = false;
}
}
@@ -1009,12 +1036,33 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
* 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 (host->bytes_left && (status & (MMCST0_DXRDY | MMCST0_DRRDY))) {
+ unsigned long im_val;
+
+ /*
+ * If interrupts fire during the following loop, they will be
+ * handled by the handler, but the PIC will still buffer these.
+ * As a result, the handler will be called again to serve these
+ * needlessly. In order to avoid these spurious interrupts,
+ * keep interrupts masked during the loop.
+ */
+ im_val = readl(host->base + DAVINCI_MMCIM);
+ writel(0, host->base + DAVINCI_MMCIM);
+
+ do {
+ davinci_fifo_data_trans(host, rw_threshold);
+ status = readl(host->base + DAVINCI_MMCST0);
+ qstatus |= status;
+ } while (host->bytes_left &&
+ (status & (MMCST0_DXRDY | MMCST0_DRRDY)));
+
+ /*
+ * If an interrupt is pending, it is assumed it will fire when
+ * it is unmasked. This assumption is also taken when the MMCIM
+ * is first set. Otherwise, writing to MMCIM after reading the
+ * status is race-prone.
+ */
+ writel(im_val, host->base + DAVINCI_MMCIM);
}
if (qstatus & MMCST0_DATDNE) {
@@ -1418,17 +1466,14 @@ static int davinci_mmcsd_suspend(struct device *dev)
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
int ret;
- mmc_host_enable(host->mmc);
ret = mmc_suspend_host(host->mmc);
if (!ret) {
writel(0, host->base + DAVINCI_MMCIM);
mmc_davinci_reset_ctrl(host, 1);
- mmc_host_disable(host->mmc);
clk_disable(host->clk);
host->suspended = 1;
} else {
host->suspended = 0;
- mmc_host_disable(host->mmc);
}
return ret;
@@ -1444,7 +1489,6 @@ static int davinci_mmcsd_resume(struct device *dev)
return 0;
clk_enable(host->clk);
- mmc_host_enable(host->mmc);
mmc_davinci_reset_ctrl(host, 0);
ret = mmc_resume_host(host->mmc);
diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c
new file mode 100644
index 0000000..dc0d25a
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-pci.c
@@ -0,0 +1,158 @@
+/*
+ * Synopsys DesignWare Multimedia Card PCI Interface driver
+ *
+ * Copyright (C) 2012 Vayavya Labs Pvt. Ltd.
+ *
+ * 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/interrupt.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/dw_mmc.h>
+#include "dw_mmc.h"
+
+#define PCI_BAR_NO 2
+#define COMPLETE_BAR 0
+#define SYNOPSYS_DW_MCI_VENDOR_ID 0x700
+#define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107
+/* Defining the Capabilities */
+#define DW_MCI_CAPABILITIES (MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED |\
+ MMC_CAP_SD_HIGHSPEED | MMC_CAP_8_BIT_DATA |\
+ MMC_CAP_SDIO_IRQ)
+
+static struct dw_mci_board pci_board_data = {
+ .num_slots = 1,
+ .caps = DW_MCI_CAPABILITIES,
+ .bus_hz = 33 * 1000 * 1000,
+ .detect_delay_ms = 200,
+ .fifo_depth = 32,
+};
+
+static int __devinit dw_mci_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *entries)
+{
+ struct dw_mci *host;
+ int ret;
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+ if (pci_request_regions(pdev, "dw_mmc_pci")) {
+ ret = -ENODEV;
+ goto err_disable_dev;
+ }
+
+ host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
+ if (!host) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+
+ host->irq = pdev->irq;
+ host->irq_flags = IRQF_SHARED;
+ host->dev = pdev->dev;
+ host->pdata = &pci_board_data;
+
+ host->regs = pci_iomap(pdev, PCI_BAR_NO, COMPLETE_BAR);
+ if (!host->regs) {
+ ret = -EIO;
+ goto err_unmap;
+ }
+
+ pci_set_drvdata(pdev, host);
+ ret = dw_mci_probe(host);
+ if (ret)
+ goto err_probe_failed;
+ return ret;
+
+err_probe_failed:
+ pci_iounmap(pdev, host->regs);
+err_unmap:
+ kfree(host);
+err_release:
+ pci_release_regions(pdev);
+err_disable_dev:
+ pci_disable_device(pdev);
+ return ret;
+}
+
+static void __devexit dw_mci_pci_remove(struct pci_dev *pdev)
+{
+ struct dw_mci *host = pci_get_drvdata(pdev);
+
+ dw_mci_remove(host);
+ pci_set_drvdata(pdev, NULL);
+ pci_release_regions(pdev);
+ pci_iounmap(pdev, host->regs);
+ kfree(host);
+ pci_disable_device(pdev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int dw_mci_pci_suspend(struct device *dev)
+{
+ int ret;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct dw_mci *host = pci_get_drvdata(pdev);
+
+ ret = dw_mci_suspend(host);
+ return ret;
+}
+
+static int dw_mci_pci_resume(struct device *dev)
+{
+ int ret;
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct dw_mci *host = pci_get_drvdata(pdev);
+
+ ret = dw_mci_resume(host);
+ return ret;
+}
+#else
+#define dw_mci_pci_suspend NULL
+#define dw_mci_pci_resume NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(dw_mci_pci_pmops, dw_mci_pci_suspend, dw_mci_pci_resume);
+
+static DEFINE_PCI_DEVICE_TABLE(dw_mci_pci_id) = {
+ { PCI_DEVICE(SYNOPSYS_DW_MCI_VENDOR_ID, SYNOPSYS_DW_MCI_DEVICE_ID) },
+ {}
+};
+MODULE_DEVICE_TABLE(pci, dw_mci_pci_id);
+
+static struct pci_driver dw_mci_pci_driver = {
+ .name = "dw_mmc_pci",
+ .id_table = dw_mci_pci_id,
+ .probe = dw_mci_pci_probe,
+ .remove = dw_mci_pci_remove,
+ .driver = {
+ .pm = &dw_mci_pci_pmops
+ },
+};
+
+static int __init dw_mci_init(void)
+{
+ return pci_register_driver(&dw_mci_pci_driver);
+}
+
+static void __exit dw_mci_exit(void)
+{
+ pci_unregister_driver(&dw_mci_pci_driver);
+}
+
+module_init(dw_mci_init);
+module_exit(dw_mci_exit);
+
+MODULE_DESCRIPTION("DW Multimedia Card PCI Interface driver");
+MODULE_AUTHOR("Shashidhar Hiremath <shashidharh@vayavyalabs.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
new file mode 100644
index 0000000..92ec3eb
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -0,0 +1,134 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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/interrupt.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/dw_mmc.h>
+#include "dw_mmc.h"
+
+static int dw_mci_pltfm_probe(struct platform_device *pdev)
+{
+ struct dw_mci *host;
+ struct resource *regs;
+ int ret;
+
+ host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
+ if (!host)
+ return -ENOMEM;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs) {
+ ret = -ENXIO;
+ goto err_free;
+ }
+
+ host->irq = platform_get_irq(pdev, 0);
+ if (host->irq < 0) {
+ ret = host->irq;
+ goto err_free;
+ }
+
+ host->dev = pdev->dev;
+ host->irq_flags = 0;
+ host->pdata = pdev->dev.platform_data;
+ ret = -ENOMEM;
+ host->regs = ioremap(regs->start, resource_size(regs));
+ if (!host->regs)
+ goto err_free;
+ platform_set_drvdata(pdev, host);
+ ret = dw_mci_probe(host);
+ if (ret)
+ goto err_out;
+ return ret;
+err_out:
+ iounmap(host->regs);
+err_free:
+ kfree(host);
+ return ret;
+}
+
+static int __exit dw_mci_pltfm_remove(struct platform_device *pdev)
+{
+ struct dw_mci *host = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ dw_mci_remove(host);
+ iounmap(host->regs);
+ kfree(host);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * TODO: we should probably disable the clock to the card in the suspend path.
+ */
+static int dw_mci_pltfm_suspend(struct device *dev)
+{
+ int ret;
+ struct dw_mci *host = dev_get_drvdata(dev);
+
+ ret = dw_mci_suspend(host);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int dw_mci_pltfm_resume(struct device *dev)
+{
+ int ret;
+ struct dw_mci *host = dev_get_drvdata(dev);
+
+ ret = dw_mci_resume(host);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+#else
+#define dw_mci_pltfm_suspend NULL
+#define dw_mci_pltfm_resume NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume);
+
+static struct platform_driver dw_mci_pltfm_driver = {
+ .remove = __exit_p(dw_mci_pltfm_remove),
+ .driver = {
+ .name = "dw_mmc",
+ .pm = &dw_mci_pltfm_pmops,
+ },
+};
+
+static int __init dw_mci_init(void)
+{
+ return platform_driver_probe(&dw_mci_pltfm_driver, dw_mci_pltfm_probe);
+}
+
+static void __exit dw_mci_exit(void)
+{
+ platform_driver_unregister(&dw_mci_pltfm_driver);
+}
+
+module_init(dw_mci_init);
+module_exit(dw_mci_exit);
+
+MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
+MODULE_AUTHOR("NXP Semiconductor VietNam");
+MODULE_AUTHOR("Imagination Technologies Ltd");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 8bec1c3..bf3c9b4 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -268,7 +268,7 @@ static void dw_mci_start_command(struct dw_mci *host,
struct mmc_command *cmd, u32 cmd_flags)
{
host->cmd = cmd;
- dev_vdbg(&host->pdev->dev,
+ dev_vdbg(&host->dev,
"start command: ARGR=0x%08x CMDR=0x%08x\n",
cmd->arg, cmd_flags);
@@ -295,15 +295,25 @@ static void dw_mci_stop_dma(struct dw_mci *host)
}
}
+static int dw_mci_get_dma_dir(struct mmc_data *data)
+{
+ if (data->flags & MMC_DATA_WRITE)
+ return DMA_TO_DEVICE;
+ else
+ return DMA_FROM_DEVICE;
+}
+
#ifdef CONFIG_MMC_DW_IDMAC
static void dw_mci_dma_cleanup(struct dw_mci *host)
{
struct mmc_data *data = host->data;
if (data)
- dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
- ((data->flags & MMC_DATA_WRITE)
- ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+ if (!data->host_cookie)
+ dma_unmap_sg(&host->dev,
+ data->sg,
+ data->sg_len,
+ dw_mci_get_dma_dir(data));
}
static void dw_mci_idmac_stop_dma(struct dw_mci *host)
@@ -326,7 +336,7 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host)
{
struct mmc_data *data = host->data;
- dev_vdbg(&host->pdev->dev, "DMA complete\n");
+ dev_vdbg(&host->dev, "DMA complete\n");
host->dma_ops->cleanup(host);
@@ -428,17 +438,15 @@ static struct dw_mci_dma_ops dw_mci_idmac_ops = {
};
#endif /* CONFIG_MMC_DW_IDMAC */
-static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
+static int dw_mci_pre_dma_transfer(struct dw_mci *host,
+ struct mmc_data *data,
+ bool next)
{
struct scatterlist *sg;
- unsigned int i, direction, sg_len;
- u32 temp;
+ unsigned int i, sg_len;
- host->using_dma = 0;
-
- /* If we don't have a channel, we can't do DMA */
- if (!host->use_dma)
- return -ENODEV;
+ if (!next && data->host_cookie)
+ return data->host_cookie;
/*
* We don't do DMA on "complex" transfers, i.e. with
@@ -447,6 +455,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
*/
if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
return -EINVAL;
+
if (data->blksz & 3)
return -EINVAL;
@@ -455,17 +464,74 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
return -EINVAL;
}
- host->using_dma = 1;
+ sg_len = dma_map_sg(&host->dev,
+ data->sg,
+ data->sg_len,
+ dw_mci_get_dma_dir(data));
+ if (sg_len == 0)
+ return -EINVAL;
- if (data->flags & MMC_DATA_READ)
- direction = DMA_FROM_DEVICE;
- else
- direction = DMA_TO_DEVICE;
+ if (next)
+ data->host_cookie = sg_len;
- sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
- direction);
+ return sg_len;
+}
- dev_vdbg(&host->pdev->dev,
+static void dw_mci_pre_req(struct mmc_host *mmc,
+ struct mmc_request *mrq,
+ bool is_first_req)
+{
+ struct dw_mci_slot *slot = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+
+ if (!slot->host->use_dma || !data)
+ return;
+
+ if (data->host_cookie) {
+ data->host_cookie = 0;
+ return;
+ }
+
+ if (dw_mci_pre_dma_transfer(slot->host, mrq->data, 1) < 0)
+ data->host_cookie = 0;
+}
+
+static void dw_mci_post_req(struct mmc_host *mmc,
+ struct mmc_request *mrq,
+ int err)
+{
+ struct dw_mci_slot *slot = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+
+ if (!slot->host->use_dma || !data)
+ return;
+
+ if (data->host_cookie)
+ dma_unmap_sg(&slot->host->dev,
+ data->sg,
+ data->sg_len,
+ dw_mci_get_dma_dir(data));
+ data->host_cookie = 0;
+}
+
+static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
+{
+ int sg_len;
+ u32 temp;
+
+ host->using_dma = 0;
+
+ /* If we don't have a channel, we can't do DMA */
+ if (!host->use_dma)
+ return -ENODEV;
+
+ sg_len = dw_mci_pre_dma_transfer(host, data, 0);
+ if (sg_len < 0)
+ return sg_len;
+
+ host->using_dma = 1;
+
+ dev_vdbg(&host->dev,
"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
sg_len);
@@ -579,8 +645,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
/* enable clock */
- mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE |
- SDMMC_CLKEN_LOW_PWR);
+ mci_writel(host, CLKENA, ((SDMMC_CLKEN_ENABLE |
+ SDMMC_CLKEN_LOW_PWR) << slot->id));
/* inform CIU */
mci_send_cmd(slot,
@@ -800,6 +866,8 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
static const struct mmc_host_ops dw_mci_ops = {
.request = dw_mci_request,
+ .pre_req = dw_mci_pre_req,
+ .post_req = dw_mci_post_req,
.set_ios = dw_mci_set_ios,
.get_ro = dw_mci_get_ro,
.get_cd = dw_mci_get_cd,
@@ -821,12 +889,12 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
slot = list_entry(host->queue.next,
struct dw_mci_slot, queue_node);
list_del(&slot->queue_node);
- dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
+ dev_vdbg(&host->dev, "list not empty: %s is next\n",
mmc_hostname(slot->mmc));
host->state = STATE_SENDING_CMD;
dw_mci_start_request(host, slot);
} else {
- dev_vdbg(&host->pdev->dev, "list empty\n");
+ dev_vdbg(&host->dev, "list empty\n");
host->state = STATE_IDLE;
}
@@ -965,7 +1033,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
data->bytes_xfered = 0;
data->error = -ETIMEDOUT;
} else {
- dev_err(&host->pdev->dev,
+ dev_err(&host->dev,
"data FIFO error "
"(status=%08x)\n",
status);
@@ -1682,7 +1750,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
struct mmc_host *mmc;
struct dw_mci_slot *slot;
- mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev);
+ mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->dev);
if (!mmc)
return -ENOMEM;
@@ -1720,13 +1788,11 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
-#ifdef CONFIG_MMC_DW_IDMAC
- mmc->max_segs = host->ring_size;
- mmc->max_blk_size = 65536;
- mmc->max_blk_count = host->ring_size;
- mmc->max_seg_size = 0x1000;
- mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
-#else
+ if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY)
+ mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
+ else
+ mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE;
+
if (host->pdata->blk_settings) {
mmc->max_segs = host->pdata->blk_settings->max_segs;
mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
@@ -1735,13 +1801,20 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
} else {
/* Useful defaults if platform data is unset. */
+#ifdef CONFIG_MMC_DW_IDMAC
+ mmc->max_segs = host->ring_size;
+ mmc->max_blk_size = 65536;
+ mmc->max_blk_count = host->ring_size;
+ mmc->max_seg_size = 0x1000;
+ mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
+#else
mmc->max_segs = 64;
mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
mmc->max_blk_count = 512;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_seg_size = mmc->max_req_size;
- }
#endif /* CONFIG_MMC_DW_IDMAC */
+ }
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
if (IS_ERR(host->vmmc)) {
@@ -1789,10 +1862,10 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
static void dw_mci_init_dma(struct dw_mci *host)
{
/* Alloc memory for sg translation */
- host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE,
+ host->sg_cpu = dma_alloc_coherent(&host->dev, PAGE_SIZE,
&host->sg_dma, GFP_KERNEL);
if (!host->sg_cpu) {
- dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n",
+ dev_err(&host->dev, "%s: could not alloc DMA memory\n",
__func__);
goto no_dma;
}
@@ -1800,7 +1873,7 @@ static void dw_mci_init_dma(struct dw_mci *host)
/* Determine which DMA interface to use */
#ifdef CONFIG_MMC_DW_IDMAC
host->dma_ops = &dw_mci_idmac_ops;
- dev_info(&host->pdev->dev, "Using internal DMA controller.\n");
+ dev_info(&host->dev, "Using internal DMA controller.\n");
#endif
if (!host->dma_ops)
@@ -1808,12 +1881,12 @@ static void dw_mci_init_dma(struct dw_mci *host)
if (host->dma_ops->init) {
if (host->dma_ops->init(host)) {
- dev_err(&host->pdev->dev, "%s: Unable to initialize "
+ dev_err(&host->dev, "%s: Unable to initialize "
"DMA Controller.\n", __func__);
goto no_dma;
}
} else {
- dev_err(&host->pdev->dev, "DMA initialization not found.\n");
+ dev_err(&host->dev, "DMA initialization not found.\n");
goto no_dma;
}
@@ -1821,7 +1894,7 @@ static void dw_mci_init_dma(struct dw_mci *host)
return;
no_dma:
- dev_info(&host->pdev->dev, "Using PIO mode.\n");
+ dev_info(&host->dev, "Using PIO mode.\n");
host->use_dma = 0;
return;
}
@@ -1847,61 +1920,37 @@ static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
return false;
}
-static int dw_mci_probe(struct platform_device *pdev)
+int dw_mci_probe(struct dw_mci *host)
{
- struct dw_mci *host;
- struct resource *regs;
- struct dw_mci_board *pdata;
- int irq, ret, i, width;
+ int width, i, ret = 0;
u32 fifo_size;
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!regs)
- return -ENXIO;
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
-
- host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
- if (!host)
- return -ENOMEM;
-
- host->pdev = pdev;
- host->pdata = pdata = pdev->dev.platform_data;
- if (!pdata || !pdata->init) {
- dev_err(&pdev->dev,
+ if (!host->pdata || !host->pdata->init) {
+ dev_err(&host->dev,
"Platform data must supply init function\n");
- ret = -ENODEV;
- goto err_freehost;
+ return -ENODEV;
}
- if (!pdata->select_slot && pdata->num_slots > 1) {
- dev_err(&pdev->dev,
+ if (!host->pdata->select_slot && host->pdata->num_slots > 1) {
+ dev_err(&host->dev,
"Platform data must supply select_slot function\n");
- ret = -ENODEV;
- goto err_freehost;
+ return -ENODEV;
}
- if (!pdata->bus_hz) {
- dev_err(&pdev->dev,
+ if (!host->pdata->bus_hz) {
+ dev_err(&host->dev,
"Platform data must supply bus speed\n");
- ret = -ENODEV;
- goto err_freehost;
+ return -ENODEV;
}
- host->bus_hz = pdata->bus_hz;
- host->quirks = pdata->quirks;
+ host->bus_hz = host->pdata->bus_hz;
+ host->quirks = host->pdata->quirks;
spin_lock_init(&host->lock);
INIT_LIST_HEAD(&host->queue);
- ret = -ENOMEM;
- host->regs = ioremap(regs->start, resource_size(regs));
- if (!host->regs)
- goto err_freehost;
- host->dma_ops = pdata->dma_ops;
+ host->dma_ops = host->pdata->dma_ops;
dw_mci_init_dma(host);
/*
@@ -1931,7 +1980,7 @@ static int dw_mci_probe(struct platform_device *pdev)
}
/* Reset all blocks */
- if (!mci_wait_reset(&pdev->dev, host)) {
+ if (!mci_wait_reset(&host->dev, host)) {
ret = -ENODEV;
goto err_dmaunmap;
}
@@ -1974,13 +2023,10 @@ static int dw_mci_probe(struct platform_device *pdev)
if (!dw_mci_card_workqueue)
goto err_dmaunmap;
INIT_WORK(&host->card_work, dw_mci_work_routine_card);
-
- ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
+ ret = request_irq(host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host);
if (ret)
goto err_workqueue;
- platform_set_drvdata(pdev, host);
-
if (host->pdata->num_slots)
host->num_slots = host->pdata->num_slots;
else
@@ -2000,7 +2046,7 @@ static int dw_mci_probe(struct platform_device *pdev)
* Need to check the version-id and set data-offset for DATA register.
*/
host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
- dev_info(&pdev->dev, "Version ID is %04x\n", host->verid);
+ dev_info(&host->dev, "Version ID is %04x\n", host->verid);
if (host->verid < DW_MMC_240A)
host->data_offset = DATA_OFFSET;
@@ -2017,12 +2063,12 @@ static int dw_mci_probe(struct platform_device *pdev)
DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
- dev_info(&pdev->dev, "DW MMC controller at irq %d, "
+ dev_info(&host->dev, "DW MMC controller at irq %d, "
"%d bit host data width, "
"%u deep fifo\n",
- irq, width, fifo_size);
+ host->irq, width, fifo_size);
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
- dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n");
+ dev_info(&host->dev, "Internal DMAC interrupt fix enabled.\n");
return 0;
@@ -2033,7 +2079,7 @@ err_init_slot:
dw_mci_cleanup_slot(host->slot[i], i);
i--;
}
- free_irq(irq, host);
+ free_irq(host->irq, host);
err_workqueue:
destroy_workqueue(dw_mci_card_workqueue);
@@ -2041,33 +2087,26 @@ err_workqueue:
err_dmaunmap:
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
- dma_free_coherent(&host->pdev->dev, PAGE_SIZE,
+ dma_free_coherent(&host->dev, PAGE_SIZE,
host->sg_cpu, host->sg_dma);
- iounmap(host->regs);
if (host->vmmc) {
regulator_disable(host->vmmc);
regulator_put(host->vmmc);
}
-
-
-err_freehost:
- kfree(host);
return ret;
}
+EXPORT_SYMBOL(dw_mci_probe);
-static int __exit dw_mci_remove(struct platform_device *pdev)
+void dw_mci_remove(struct dw_mci *host)
{
- struct dw_mci *host = platform_get_drvdata(pdev);
int i;
mci_writel(host, RINTSTS, 0xFFFFFFFF);
mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
- platform_set_drvdata(pdev, NULL);
-
for (i = 0; i < host->num_slots; i++) {
- dev_dbg(&pdev->dev, "remove slot %d\n", i);
+ dev_dbg(&host->dev, "remove slot %d\n", i);
if (host->slot[i])
dw_mci_cleanup_slot(host->slot[i], i);
}
@@ -2076,9 +2115,9 @@ static int __exit dw_mci_remove(struct platform_device *pdev)
mci_writel(host, CLKENA, 0);
mci_writel(host, CLKSRC, 0);
- free_irq(platform_get_irq(pdev, 0), host);
+ free_irq(host->irq, host);
destroy_workqueue(dw_mci_card_workqueue);
- dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+ dma_free_coherent(&host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
@@ -2088,20 +2127,18 @@ static int __exit dw_mci_remove(struct platform_device *pdev)
regulator_put(host->vmmc);
}
- iounmap(host->regs);
-
- kfree(host);
- return 0;
}
+EXPORT_SYMBOL(dw_mci_remove);
+
+
#ifdef CONFIG_PM_SLEEP
/*
* TODO: we should probably disable the clock to the card in the suspend path.
*/
-static int dw_mci_suspend(struct device *dev)
+int dw_mci_suspend(struct dw_mci *host)
{
- int i, ret;
- struct dw_mci *host = dev_get_drvdata(dev);
+ int i, ret = 0;
for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i];
@@ -2123,11 +2160,11 @@ static int dw_mci_suspend(struct device *dev)
return 0;
}
+EXPORT_SYMBOL(dw_mci_suspend);
-static int dw_mci_resume(struct device *dev)
+int dw_mci_resume(struct dw_mci *host)
{
int i, ret;
- struct dw_mci *host = dev_get_drvdata(dev);
if (host->vmmc)
regulator_enable(host->vmmc);
@@ -2135,7 +2172,7 @@ static int dw_mci_resume(struct device *dev)
if (host->dma_ops->init)
host->dma_ops->init(host);
- if (!mci_wait_reset(dev, host)) {
+ if (!mci_wait_reset(&host->dev, host)) {
ret = -ENODEV;
return ret;
}
@@ -2157,32 +2194,19 @@ static int dw_mci_resume(struct device *dev)
if (ret < 0)
return ret;
}
-
return 0;
}
-#else
-#define dw_mci_suspend NULL
-#define dw_mci_resume NULL
+EXPORT_SYMBOL(dw_mci_resume);
#endif /* CONFIG_PM_SLEEP */
-static SIMPLE_DEV_PM_OPS(dw_mci_pmops, dw_mci_suspend, dw_mci_resume);
-
-static struct platform_driver dw_mci_driver = {
- .remove = __exit_p(dw_mci_remove),
- .driver = {
- .name = "dw_mmc",
- .pm = &dw_mci_pmops,
- },
-};
-
static int __init dw_mci_init(void)
{
- return platform_driver_probe(&dw_mci_driver, dw_mci_probe);
+ printk(KERN_INFO "Synopsys Designware Multimedia Card Interface Driver");
+ return 0;
}
static void __exit dw_mci_exit(void)
{
- platform_driver_unregister(&dw_mci_driver);
}
module_init(dw_mci_init);
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index df392a1..15c27e1 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -175,4 +175,11 @@
(*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value))
#endif
+extern int dw_mci_probe(struct dw_mci *host);
+extern void dw_mci_remove(struct dw_mci *host);
+#ifdef CONFIG_PM
+extern int dw_mci_suspend(struct dw_mci *host);
+extern int dw_mci_resume(struct dw_mci *host);
+#endif
+
#endif /* _DW_MMC_H_ */
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 983e244..032b847 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -30,6 +30,7 @@
#include <linux/dma-mapping.h>
#include <linux/amba/mmci.h>
#include <linux/pm_runtime.h>
+#include <linux/types.h>
#include <asm/div64.h>
#include <asm/io.h>
@@ -400,6 +401,7 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.src_maxburst = variant->fifohalfsize >> 2, /* # of words */
.dst_maxburst = variant->fifohalfsize >> 2, /* # of words */
+ .device_fc = false,
};
struct dma_chan *chan;
struct dma_device *device;
@@ -441,7 +443,7 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
return -EINVAL;
dmaengine_slave_config(chan, &conf);
- desc = device->device_prep_slave_sg(chan, data->sg, nr_sg,
+ desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg,
conf.direction, DMA_CTRL_ACK);
if (!desc)
goto unmap_exit;
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index 4184b79..b2058b4 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -33,6 +33,7 @@
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/dmaengine.h>
+#include <linux/types.h>
#include <asm/dma.h>
#include <asm/irq.h>
@@ -254,7 +255,7 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
if (nents != data->sg_len)
return -EINVAL;
- host->desc = host->dma->device->device_prep_slave_sg(host->dma,
+ host->desc = dmaengine_prep_slave_sg(host->dma,
data->sg, data->sg_len, slave_dirn,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
@@ -267,6 +268,7 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
wmb();
dmaengine_submit(host->desc);
+ dma_async_issue_pending(host->dma);
return 0;
}
@@ -710,6 +712,7 @@ static int mxcmci_setup_dma(struct mmc_host *mmc)
config->src_addr_width = 4;
config->dst_maxburst = host->burstlen;
config->src_maxburst = host->burstlen;
+ config->device_fc = false;
return dmaengine_slave_config(host->dma, config);
}
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index 382c835..b0f2ef9 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -38,10 +38,10 @@
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/module.h>
+#include <linux/fsl/mxs-dma.h>
#include <mach/mxs.h>
#include <mach/common.h>
-#include <mach/dma.h>
#include <mach/mmc.h>
#define DRIVER_NAME "mxs-mmc"
@@ -305,7 +305,7 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
}
static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
- struct mxs_mmc_host *host, unsigned int append)
+ struct mxs_mmc_host *host, unsigned long flags)
{
struct dma_async_tx_descriptor *desc;
struct mmc_data *data = host->data;
@@ -324,8 +324,8 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
sg_len = SSP_PIO_NUM;
}
- desc = host->dmach->device->device_prep_slave_sg(host->dmach,
- sgl, sg_len, host->slave_dirn, append);
+ desc = dmaengine_prep_slave_sg(host->dmach,
+ sgl, sg_len, host->slave_dirn, flags);
if (desc) {
desc->callback = mxs_mmc_dma_irq_callback;
desc->callback_param = host;
@@ -358,7 +358,7 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host)
host->ssp_pio_words[2] = cmd1;
host->dma_dir = DMA_NONE;
host->slave_dirn = DMA_TRANS_NONE;
- desc = mxs_mmc_prep_dma(host, 0);
+ desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
if (!desc)
goto out;
@@ -398,7 +398,7 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host)
host->ssp_pio_words[2] = cmd1;
host->dma_dir = DMA_NONE;
host->slave_dirn = DMA_TRANS_NONE;
- desc = mxs_mmc_prep_dma(host, 0);
+ desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
if (!desc)
goto out;
@@ -526,7 +526,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
host->data = data;
host->dma_dir = dma_data_dir;
host->slave_dirn = slave_dirn;
- desc = mxs_mmc_prep_dma(host, 1);
+ desc = mxs_mmc_prep_dma(host, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc)
goto out;
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index fd0c661..47adb16 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -26,6 +26,9 @@
#include <linux/platform_device.h>
#include <linux/timer.h>
#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
#include <linux/mmc/host.h>
#include <linux/mmc/core.h>
#include <linux/mmc/mmc.h>
@@ -106,17 +109,6 @@
#define SOFTRESET (1 << 1)
#define RESETDONE (1 << 0)
-/*
- * FIXME: Most likely all the data using these _DEVID defines should come
- * from the platform_data, or implemented in controller and slot specific
- * functions.
- */
-#define OMAP_MMC1_DEVID 0
-#define OMAP_MMC2_DEVID 1
-#define OMAP_MMC3_DEVID 2
-#define OMAP_MMC4_DEVID 3
-#define OMAP_MMC5_DEVID 4
-
#define MMC_AUTOSUSPEND_DELAY 100
#define MMC_TIMEOUT_MS 20
#define OMAP_MMC_MIN_CLOCK 400000
@@ -164,7 +156,6 @@ struct omap_hsmmc_host {
void __iomem *base;
resource_size_t mapbase;
spinlock_t irq_lock; /* Prevent races with irq handler */
- unsigned int id;
unsigned int dma_len;
unsigned int dma_sg_idx;
unsigned char bus_mode;
@@ -179,7 +170,6 @@ struct omap_hsmmc_host {
int got_dbclk;
int response_busy;
int context_loss;
- int dpm_state;
int vdd;
int protect_card;
int reqs_blocked;
@@ -241,28 +231,7 @@ static int omap_hsmmc_resume_cdirq(struct device *dev, int slot)
#ifdef CONFIG_REGULATOR
-static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on,
- int vdd)
-{
- struct omap_hsmmc_host *host =
- platform_get_drvdata(to_platform_device(dev));
- int ret;
-
- if (mmc_slot(host).before_set_reg)
- mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
-
- if (power_on)
- ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
- else
- ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
-
- if (mmc_slot(host).after_set_reg)
- mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
-
- return ret;
-}
-
-static int omap_hsmmc_235_set_power(struct device *dev, int slot, int power_on,
+static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
int vdd)
{
struct omap_hsmmc_host *host =
@@ -275,6 +244,13 @@ static int omap_hsmmc_235_set_power(struct device *dev, int slot, int power_on,
*/
if (!host->vcc)
return 0;
+ /*
+ * With DT, never turn OFF the regulator. This is because
+ * the pbias cell programming support is still missing when
+ * booting with Device tree
+ */
+ if (of_have_populated_dt() && !vdd)
+ return 0;
if (mmc_slot(host).before_set_reg)
mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
@@ -318,106 +294,16 @@ static int omap_hsmmc_235_set_power(struct device *dev, int slot, int power_on,
return ret;
}
-static int omap_hsmmc_4_set_power(struct device *dev, int slot, int power_on,
- int vdd)
-{
- return 0;
-}
-
-static int omap_hsmmc_1_set_sleep(struct device *dev, int slot, int sleep,
- int vdd, int cardsleep)
-{
- struct omap_hsmmc_host *host =
- platform_get_drvdata(to_platform_device(dev));
- int mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
-
- return regulator_set_mode(host->vcc, mode);
-}
-
-static int omap_hsmmc_235_set_sleep(struct device *dev, int slot, int sleep,
- int vdd, int cardsleep)
-{
- struct omap_hsmmc_host *host =
- platform_get_drvdata(to_platform_device(dev));
- int err, mode;
-
- /*
- * If we don't see a Vcc regulator, assume it's a fixed
- * voltage always-on regulator.
- */
- if (!host->vcc)
- return 0;
-
- mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
-
- if (!host->vcc_aux)
- return regulator_set_mode(host->vcc, mode);
-
- if (cardsleep) {
- /* VCC can be turned off if card is asleep */
- if (sleep)
- err = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
- else
- err = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
- } else
- err = regulator_set_mode(host->vcc, mode);
- if (err)
- return err;
-
- if (!mmc_slot(host).vcc_aux_disable_is_sleep)
- return regulator_set_mode(host->vcc_aux, mode);
-
- if (sleep)
- return regulator_disable(host->vcc_aux);
- else
- return regulator_enable(host->vcc_aux);
-}
-
-static int omap_hsmmc_4_set_sleep(struct device *dev, int slot, int sleep,
- int vdd, int cardsleep)
-{
- return 0;
-}
-
static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
{
struct regulator *reg;
- int ret = 0;
int ocr_value = 0;
- switch (host->id) {
- case OMAP_MMC1_DEVID:
- /* On-chip level shifting via PBIAS0/PBIAS1 */
- mmc_slot(host).set_power = omap_hsmmc_1_set_power;
- mmc_slot(host).set_sleep = omap_hsmmc_1_set_sleep;
- break;
- case OMAP_MMC2_DEVID:
- case OMAP_MMC3_DEVID:
- case OMAP_MMC5_DEVID:
- /* Off-chip level shifting, or none */
- mmc_slot(host).set_power = omap_hsmmc_235_set_power;
- mmc_slot(host).set_sleep = omap_hsmmc_235_set_sleep;
- break;
- case OMAP_MMC4_DEVID:
- mmc_slot(host).set_power = omap_hsmmc_4_set_power;
- mmc_slot(host).set_sleep = omap_hsmmc_4_set_sleep;
- default:
- pr_err("MMC%d configuration not supported!\n", host->id);
- return -EINVAL;
- }
+ mmc_slot(host).set_power = omap_hsmmc_set_power;
reg = regulator_get(host->dev, "vmmc");
if (IS_ERR(reg)) {
dev_dbg(host->dev, "vmmc regulator missing\n");
- /*
- * HACK: until fixed.c regulator is usable,
- * we don't require a main regulator
- * for MMC2 or MMC3
- */
- if (host->id == OMAP_MMC1_DEVID) {
- ret = PTR_ERR(reg);
- goto err;
- }
} else {
host->vcc = reg;
ocr_value = mmc_regulator_get_ocrmask(reg);
@@ -425,8 +311,8 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
mmc_slot(host).ocr_mask = ocr_value;
} else {
if (!(mmc_slot(host).ocr_mask & ocr_value)) {
- pr_err("MMC%d ocrmask %x is not supported\n",
- host->id, mmc_slot(host).ocr_mask);
+ dev_err(host->dev, "ocrmask %x is not supported\n",
+ mmc_slot(host).ocr_mask);
mmc_slot(host).ocr_mask = 0;
return -EINVAL;
}
@@ -459,11 +345,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
}
return 0;
-
-err:
- mmc_slot(host).set_power = NULL;
- mmc_slot(host).set_sleep = NULL;
- return ret;
}
static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
@@ -471,7 +352,6 @@ static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
regulator_put(host->vcc);
regulator_put(host->vcc_aux);
mmc_slot(host).set_power = NULL;
- mmc_slot(host).set_sleep = NULL;
}
static inline int omap_hsmmc_have_reg(void)
@@ -710,7 +590,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
- if (host->id == OMAP_MMC1_DEVID) {
+ if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
if (host->power_mode != MMC_POWER_OFF &&
(1 << ios->vdd) <= MMC_VDD_23_24)
hctl = SDVS18;
@@ -1261,14 +1141,14 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
host->reqs_blocked = 0;
if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) {
if (host->protect_card) {
- pr_info("%s: cover is closed, "
+ dev_info(host->dev, "%s: cover is closed, "
"card is now accessible\n",
mmc_hostname(host->mmc));
host->protect_card = 0;
}
} else {
if (!host->protect_card) {
- pr_info("%s: cover is open, "
+ dev_info(host->dev, "%s: cover is open, "
"card is now inaccessible\n",
mmc_hostname(host->mmc));
host->protect_card = 1;
@@ -1405,7 +1285,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
if (!next && data->host_cookie &&
data->host_cookie != host->next_data.cookie) {
- pr_warning("[%s] invalid cookie: data->host_cookie %d"
+ dev_warn(host->dev, "[%s] invalid cookie: data->host_cookie %d"
" host->next_data.cookie %d\n",
__func__, data->host_cookie, host->next_data.cookie);
data->host_cookie = 0;
@@ -1663,7 +1543,13 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* of external transceiver; but they all handle 1.8V.
*/
if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) &&
- (ios->vdd == DUAL_VOLT_OCR_BIT)) {
+ (ios->vdd == DUAL_VOLT_OCR_BIT) &&
+ /*
+ * With pbias cell programming missing, this
+ * can't be allowed when booting with device
+ * tree.
+ */
+ (!of_have_populated_dt())) {
/*
* The mmc_select_voltage fn of the core does
* not seem to set the power_mode to
@@ -1748,7 +1634,7 @@ static int omap_hsmmc_enable_fclk(struct mmc_host *mmc)
return 0;
}
-static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy)
+static int omap_hsmmc_disable_fclk(struct mmc_host *mmc)
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
@@ -1782,15 +1668,8 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
if (host->pdata->get_context_loss_count)
context_loss = host->pdata->get_context_loss_count(host->dev);
- seq_printf(s, "mmc%d:\n"
- " enabled:\t%d\n"
- " dpm_state:\t%d\n"
- " nesting_cnt:\t%d\n"
- " ctx_loss:\t%d:%d\n"
- "\nregs:\n",
- mmc->index, mmc->enabled ? 1 : 0,
- host->dpm_state, mmc->nesting_cnt,
- host->context_loss, context_loss);
+ seq_printf(s, "mmc%d:\n ctx_loss:\t%d:%d\n\nregs:\n",
+ mmc->index, host->context_loss, context_loss);
if (host->suspended) {
seq_printf(s, "host suspended, can't read registers\n");
@@ -1847,6 +1726,65 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc)
#endif
+#ifdef CONFIG_OF
+static u16 omap4_reg_offset = 0x100;
+
+static const struct of_device_id omap_mmc_of_match[] = {
+ {
+ .compatible = "ti,omap2-hsmmc",
+ },
+ {
+ .compatible = "ti,omap3-hsmmc",
+ },
+ {
+ .compatible = "ti,omap4-hsmmc",
+ .data = &omap4_reg_offset,
+ },
+ {},
+}
+MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
+
+static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
+{
+ struct omap_mmc_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+ u32 bus_width;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL; /* out of memory */
+
+ if (of_find_property(np, "ti,dual-volt", NULL))
+ pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
+
+ /* This driver only supports 1 slot */
+ pdata->nr_slots = 1;
+ pdata->slots[0].switch_pin = of_get_named_gpio(np, "cd-gpios", 0);
+ pdata->slots[0].gpio_wp = of_get_named_gpio(np, "wp-gpios", 0);
+
+ if (of_find_property(np, "ti,non-removable", NULL)) {
+ pdata->slots[0].nonremovable = true;
+ pdata->slots[0].no_regulator_off_init = true;
+ }
+ of_property_read_u32(np, "ti,bus-width", &bus_width);
+ if (bus_width == 4)
+ pdata->slots[0].caps |= MMC_CAP_4_BIT_DATA;
+ else if (bus_width == 8)
+ pdata->slots[0].caps |= MMC_CAP_8_BIT_DATA;
+
+ if (of_find_property(np, "ti,needs-special-reset", NULL))
+ pdata->slots[0].features |= HSMMC_HAS_UPDATED_RESET;
+
+ return pdata;
+}
+#else
+static inline struct omap_mmc_platform_data
+ *of_get_hsmmc_pdata(struct device *dev)
+{
+ return NULL;
+}
+#endif
+
static int __init omap_hsmmc_probe(struct platform_device *pdev)
{
struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
@@ -1854,6 +1792,16 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
struct omap_hsmmc_host *host = NULL;
struct resource *res;
int ret, irq;
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
+ if (match) {
+ pdata = of_get_hsmmc_pdata(&pdev->dev);
+ if (match->data) {
+ u16 *offsetp = match->data;
+ pdata->reg_offset = *offsetp;
+ }
+ }
if (pdata == NULL) {
dev_err(&pdev->dev, "Platform Data is missing\n");
@@ -1894,7 +1842,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
host->dev->dma_mask = &pdata->dma_mask;
host->dma_ch = -1;
host->irq = irq;
- host->id = pdev->id;
host->slot_id = 0;
host->mapbase = res->start;
host->base = ioremap(host->mapbase, SZ_4K);
@@ -1912,8 +1859,12 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
if (mmc_slot(host).vcc_aux_disable_is_sleep)
mmc_slot(host).no_off = 1;
- mmc->f_min = OMAP_MMC_MIN_CLOCK;
- mmc->f_max = OMAP_MMC_MAX_CLOCK;
+ mmc->f_min = OMAP_MMC_MIN_CLOCK;
+
+ if (pdata->max_freq > 0)
+ mmc->f_max = pdata->max_freq;
+ else
+ mmc->f_max = OMAP_MMC_MAX_CLOCK;
spin_lock_init(&host->irq_lock);
@@ -1926,7 +1877,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_context_save(host);
- mmc->caps |= MMC_CAP_DISABLE;
if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n");
mmc->caps2 |= MMC_CAP2_NO_MULTI_READ;
@@ -1977,32 +1927,19 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_conf_bus_power(host);
- /* Select DMA lines */
- switch (host->id) {
- case OMAP_MMC1_DEVID:
- host->dma_line_tx = OMAP24XX_DMA_MMC1_TX;
- host->dma_line_rx = OMAP24XX_DMA_MMC1_RX;
- break;
- case OMAP_MMC2_DEVID:
- host->dma_line_tx = OMAP24XX_DMA_MMC2_TX;
- host->dma_line_rx = OMAP24XX_DMA_MMC2_RX;
- break;
- case OMAP_MMC3_DEVID:
- host->dma_line_tx = OMAP34XX_DMA_MMC3_TX;
- host->dma_line_rx = OMAP34XX_DMA_MMC3_RX;
- break;
- case OMAP_MMC4_DEVID:
- host->dma_line_tx = OMAP44XX_DMA_MMC4_TX;
- host->dma_line_rx = OMAP44XX_DMA_MMC4_RX;
- break;
- case OMAP_MMC5_DEVID:
- host->dma_line_tx = OMAP44XX_DMA_MMC5_TX;
- host->dma_line_rx = OMAP44XX_DMA_MMC5_RX;
- break;
- default:
- dev_err(mmc_dev(host->mmc), "Invalid MMC id\n");
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
+ if (!res) {
+ dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
+ goto err_irq;
+ }
+ host->dma_line_tx = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
+ if (!res) {
+ dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
goto err_irq;
}
+ host->dma_line_rx = res->start;
/* Request IRQ for MMC operations */
ret = request_irq(host->irq, omap_hsmmc_irq, 0,
@@ -2083,6 +2020,7 @@ err_irq_cd_init:
err_irq:
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
+ pm_runtime_disable(host->dev);
clk_put(host->fclk);
if (host->got_dbclk) {
clk_disable(host->dbclk);
@@ -2269,6 +2207,7 @@ static struct platform_driver omap_hsmmc_driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.pm = &omap_hsmmc_dev_pm_ops,
+ .of_match_table = of_match_ptr(omap_mmc_of_match),
},
};
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 5d876ff..f8eb1fb 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -1,7 +1,7 @@
/*
* Freescale eSDHC controller driver.
*
- * Copyright (c) 2007, 2010 Freescale Semiconductor, Inc.
+ * Copyright (c) 2007, 2010, 2012 Freescale Semiconductor, Inc.
* Copyright (c) 2009 MontaVista Software, Inc.
*
* Authors: Xiaobo Xie <X.Xie@freescale.com>
@@ -14,6 +14,7 @@
*/
#include <linux/io.h>
+#include <linux/of.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/mmc/host.h>
@@ -114,6 +115,34 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
return pltfm_host->clock / 256 / 16;
}
+static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ /* Workaround to reduce the clock frequency for p1010 esdhc */
+ if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
+ if (clock > 20000000)
+ clock -= 5000000;
+ if (clock > 40000000)
+ clock -= 5000000;
+ }
+
+ /* Set the clock */
+ esdhc_set_clock(host, clock);
+}
+
+#ifdef CONFIG_PM
+static u32 esdhc_proctl;
+static void esdhc_of_suspend(struct sdhci_host *host)
+{
+ esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL);
+}
+
+static void esdhc_of_resume(struct sdhci_host *host)
+{
+ esdhc_of_enable_dma(host);
+ sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL);
+}
+#endif
+
static struct sdhci_ops sdhci_esdhc_ops = {
.read_l = sdhci_be32bs_readl,
.read_w = esdhc_readw,
@@ -121,10 +150,14 @@ static struct sdhci_ops sdhci_esdhc_ops = {
.write_l = sdhci_be32bs_writel,
.write_w = esdhc_writew,
.write_b = esdhc_writeb,
- .set_clock = esdhc_set_clock,
+ .set_clock = esdhc_of_set_clock,
.enable_dma = esdhc_of_enable_dma,
.get_max_clock = esdhc_of_get_max_clock,
.get_min_clock = esdhc_of_get_min_clock,
+#ifdef CONFIG_PM
+ .platform_suspend = esdhc_of_suspend,
+ .platform_resume = esdhc_of_resume,
+#endif
};
static struct sdhci_pltfm_data sdhci_esdhc_pdata = {
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 6ebdc40..fbbebe2 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -29,6 +29,12 @@
#include "sdhci.h"
/*
+ * PCI device IDs
+ */
+#define PCI_DEVICE_ID_INTEL_PCH_SDIO0 0x8809
+#define PCI_DEVICE_ID_INTEL_PCH_SDIO1 0x880a
+
+/*
* PCI registers
*/
@@ -47,6 +53,7 @@ struct sdhci_pci_slot;
struct sdhci_pci_fixes {
unsigned int quirks;
+ unsigned int quirks2;
bool allow_runtime_pm;
int (*probe) (struct sdhci_pci_chip *);
@@ -73,6 +80,7 @@ struct sdhci_pci_chip {
struct pci_dev *pdev;
unsigned int quirks;
+ unsigned int quirks2;
bool allow_runtime_pm;
const struct sdhci_pci_fixes *fixes;
@@ -172,6 +180,12 @@ static int mrst_hc_probe(struct sdhci_pci_chip *chip)
return 0;
}
+static int pch_hc_probe_slot(struct sdhci_pci_slot *slot)
+{
+ slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+ return 0;
+}
+
#ifdef CONFIG_PM_RUNTIME
static irqreturn_t sdhci_pci_sd_cd(int irq, void *dev_id)
@@ -244,7 +258,8 @@ static inline void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot)
static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
{
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;
- slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC;
+ slot->host->mmc->caps2 |= MMC_CAP2_BOOTPART_NOACC |
+ MMC_CAP2_HC_ERASE_SZ;
return 0;
}
@@ -271,6 +286,7 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+ .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
.allow_runtime_pm = true,
.probe_slot = mfd_sdio_probe_slot,
};
@@ -281,6 +297,11 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = {
.probe_slot = mfd_emmc_probe_slot,
};
+static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = {
+ .quirks = SDHCI_QUIRK_BROKEN_ADMA,
+ .probe_slot = pch_hc_probe_slot,
+};
+
/* O2Micro extra registers */
#define O2_SD_LOCK_WP 0xD3
#define O2_SD_MULTI_VCC3V 0xEE
@@ -817,6 +838,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
},
{
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_PCH_SDIO0,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_pch_sdio,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_PCH_SDIO1,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_pch_sdio,
+ },
+
+ {
.vendor = PCI_VENDOR_ID_O2,
.device = PCI_DEVICE_ID_O2_8120,
.subvendor = PCI_ANY_ID,
@@ -1206,6 +1243,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
host->hw_name = "PCI";
host->ops = &sdhci_pci_ops;
host->quirks = chip->quirks;
+ host->quirks2 = chip->quirks2;
host->irq = pdev->irq;
@@ -1365,6 +1403,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data;
if (chip->fixes) {
chip->quirks = chip->fixes->quirks;
+ chip->quirks2 = chip->fixes->quirks2;
chip->allow_runtime_pm = chip->fixes->allow_runtime_pm;
}
chip->num_slots = slots;
@@ -1379,6 +1418,8 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
slots = chip->num_slots; /* Quirk may have changed this */
+ pci_enable_msi(pdev);
+
for (i = 0; i < slots; i++) {
slot = sdhci_pci_probe_slot(pdev, chip, first_bar, i);
if (IS_ERR(slot)) {
@@ -1397,6 +1438,8 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
return 0;
free:
+ pci_disable_msi(pdev);
+
pci_set_drvdata(pdev, NULL);
kfree(chip);
@@ -1419,6 +1462,8 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
for (i = 0; i < chip->num_slots; i++)
sdhci_pci_remove_slot(chip->slots[i]);
+ pci_disable_msi(pdev);
+
pci_set_drvdata(pdev, NULL);
kfree(chip);
}
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c
index b7f8b33..6dfa82e 100644
--- a/drivers/mmc/host/sdhci-spear.c
+++ b/drivers/mmc/host/sdhci-spear.c
@@ -300,20 +300,15 @@ static int sdhci_resume(struct device *dev)
return sdhci_resume_host(host);
}
-
-const struct dev_pm_ops sdhci_pm_ops = {
- .suspend = sdhci_suspend,
- .resume = sdhci_resume,
-};
#endif
+static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume);
+
static struct platform_driver sdhci_driver = {
.driver = {
.name = "sdhci",
.owner = THIS_MODULE,
-#ifdef CONFIG_PM
.pm = &sdhci_pm_ops,
-#endif
},
.probe = sdhci_probe,
.remove = __devexit_p(sdhci_remove),
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index cb34856..53b2650 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -19,6 +19,7 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/mmc/card.h>
@@ -31,6 +32,19 @@
#include "sdhci-pltfm.h"
+#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
+#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
+
+struct sdhci_tegra_soc_data {
+ struct sdhci_pltfm_data *pdata;
+ u32 nvquirks;
+};
+
+struct sdhci_tegra {
+ const struct tegra_sdhci_platform_data *plat;
+ const struct sdhci_tegra_soc_data *soc_data;
+};
+
static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
{
u32 val;
@@ -46,7 +60,12 @@ static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
{
- if (unlikely(reg == SDHCI_HOST_VERSION)) {
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
+
+ if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
+ (reg == SDHCI_HOST_VERSION))) {
/* Erratum: Version register is invalid in HW. */
return SDHCI_SPEC_200;
}
@@ -56,6 +75,10 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
+
/* Seems like we're getting spurious timeout and crc errors, so
* disable signalling of them. In case of real errors software
* timers should take care of eventually detecting them.
@@ -65,7 +88,8 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
writel(val, host->ioaddr + reg);
- if (unlikely(reg == SDHCI_INT_ENABLE)) {
+ if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
+ (reg == SDHCI_INT_ENABLE))) {
/* Erratum: Must enable block gap interrupt detection */
u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
if (val & SDHCI_INT_CARD_INT)
@@ -76,10 +100,11 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
}
}
-static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci)
+static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
- struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
if (!gpio_is_valid(plat->wp_gpio))
return -1;
@@ -98,7 +123,8 @@ static irqreturn_t carddetect_irq(int irq, void *data)
static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
u32 ctrl;
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
@@ -124,16 +150,44 @@ static struct sdhci_ops tegra_sdhci_ops = {
.platform_8bit_width = tegra_sdhci_8bit,
};
-static struct sdhci_pltfm_data sdhci_tegra_pdata = {
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+static struct sdhci_pltfm_data sdhci_tegra20_pdata = {
+ .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+ SDHCI_QUIRK_SINGLE_POWER_WRITE |
+ SDHCI_QUIRK_NO_HISPD_BIT |
+ SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
+ .ops = &tegra_sdhci_ops,
+};
+
+static struct sdhci_tegra_soc_data soc_data_tegra20 = {
+ .pdata = &sdhci_tegra20_pdata,
+ .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
+ NVQUIRK_ENABLE_BLOCK_GAP_DET,
+};
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+static struct sdhci_pltfm_data sdhci_tegra30_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
.ops = &tegra_sdhci_ops,
};
+static struct sdhci_tegra_soc_data soc_data_tegra30 = {
+ .pdata = &sdhci_tegra30_pdata,
+};
+#endif
+
static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
- { .compatible = "nvidia,tegra20-sdhci", },
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
+#endif
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
+#endif
{}
};
MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
@@ -164,13 +218,22 @@ static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata(
static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
{
+ const struct of_device_id *match;
+ const struct sdhci_tegra_soc_data *soc_data;
+ struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct tegra_sdhci_platform_data *plat;
- struct sdhci_host *host;
+ struct sdhci_tegra *tegra_host;
struct clk *clk;
int rc;
- host = sdhci_pltfm_init(pdev, &sdhci_tegra_pdata);
+ match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
+ if (match)
+ soc_data = match->data;
+ else
+ soc_data = &soc_data_tegra20;
+
+ host = sdhci_pltfm_init(pdev, soc_data->pdata);
if (IS_ERR(host))
return PTR_ERR(host);
@@ -187,7 +250,17 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
goto err_no_plat;
}
- pltfm_host->priv = plat;
+ tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL);
+ if (!tegra_host) {
+ dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n");
+ rc = -ENOMEM;
+ goto err_no_plat;
+ }
+
+ tegra_host->plat = plat;
+ tegra_host->soc_data = soc_data;
+
+ pltfm_host->priv = tegra_host;
if (gpio_is_valid(plat->power_gpio)) {
rc = gpio_request(plat->power_gpio, "sdhci_power");
@@ -283,7 +356,8 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct tegra_sdhci_platform_data *plat = pltfm_host->priv;
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead);
@@ -326,5 +400,5 @@ static struct platform_driver sdhci_tegra_driver = {
module_platform_driver(sdhci_tegra_driver);
MODULE_DESCRIPTION("SDHCI driver for Tegra");
-MODULE_AUTHOR(" Google, Inc.");
+MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 8d66706..8262cad 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2267,8 +2267,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
{
irqreturn_t result;
struct sdhci_host *host = dev_id;
- u32 intmask;
- int cardint = 0;
+ u32 intmask, unexpected = 0;
+ int cardint = 0, max_loops = 16;
spin_lock(&host->lock);
@@ -2286,6 +2286,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
goto out;
}
+again:
DBG("*** %s got interrupt: 0x%08x\n",
mmc_hostname(host->mmc), intmask);
@@ -2344,19 +2345,23 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
intmask &= ~SDHCI_INT_CARD_INT;
if (intmask) {
- pr_err("%s: Unexpected interrupt 0x%08x.\n",
- mmc_hostname(host->mmc), intmask);
- sdhci_dumpregs(host);
-
+ unexpected |= intmask;
sdhci_writel(host, intmask, SDHCI_INT_STATUS);
}
result = IRQ_HANDLED;
- mmiowb();
+ intmask = sdhci_readl(host, SDHCI_INT_STATUS);
+ if (intmask && --max_loops)
+ goto again;
out:
spin_unlock(&host->lock);
+ if (unexpected) {
+ pr_err("%s: Unexpected interrupt 0x%08x.\n",
+ mmc_hostname(host->mmc), unexpected);
+ sdhci_dumpregs(host);
+ }
/*
* We have to delay this as it calls back into the driver.
*/
@@ -2379,6 +2384,9 @@ int sdhci_suspend_host(struct sdhci_host *host)
int ret;
bool has_tuning_timer;
+ if (host->ops->platform_suspend)
+ host->ops->platform_suspend(host);
+
sdhci_disable_card_detection(host);
/* Disable tuning since we are suspending */
@@ -2423,12 +2431,24 @@ int sdhci_resume_host(struct sdhci_host *host)
if (ret)
return ret;
- sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER));
- mmiowb();
+ if ((host->mmc->pm_flags & MMC_PM_KEEP_POWER) &&
+ (host->quirks2 & SDHCI_QUIRK2_HOST_OFF_CARD_ON)) {
+ /* Card keeps power but host controller does not */
+ sdhci_init(host, 0);
+ host->pwr = 0;
+ host->clock = 0;
+ sdhci_do_set_ios(host, &host->mmc->ios);
+ } else {
+ sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER));
+ mmiowb();
+ }
ret = mmc_resume_host(host->mmc);
sdhci_enable_card_detection(host);
+ if (host->ops->platform_resume)
+ host->ops->platform_resume(host);
+
/* Set the re-tuning expiration flag */
if ((host->version >= SDHCI_SPEC_300) && host->tuning_count &&
(host->tuning_mode == SDHCI_TUNING_MODE_1))
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index ad265b9..f761f23 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -275,6 +275,8 @@ struct sdhci_ops {
void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
void (*hw_reset)(struct sdhci_host *host);
+ void (*platform_suspend)(struct sdhci_host *host);
+ void (*platform_resume)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index 75a4854..aafaf0b 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -286,7 +286,7 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
DMA_FROM_DEVICE);
if (ret > 0) {
host->dma_active = true;
- desc = chan->device->device_prep_slave_sg(chan, sg, ret,
+ desc = dmaengine_prep_slave_sg(chan, sg, ret,
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
}
@@ -335,7 +335,7 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
DMA_TO_DEVICE);
if (ret > 0) {
host->dma_active = true;
- desc = chan->device->device_prep_slave_sg(chan, sg, ret,
+ desc = dmaengine_prep_slave_sg(chan, sg, ret,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
}
@@ -746,7 +746,6 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
case MMC_SET_WRITE_PROT:
case MMC_CLR_WRITE_PROT:
case MMC_ERASE:
- case MMC_GEN_CMD:
tmp |= CMD_SET_RBSY;
break;
}
@@ -829,7 +828,6 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
case MMC_SET_WRITE_PROT:
case MMC_CLR_WRITE_PROT:
case MMC_ERASE:
- case MMC_GEN_CMD:
mask = MASK_START_CMD | MASK_MRBSYE;
break;
default:
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index 58da3c4..934b68e 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -90,6 +90,15 @@ static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
return 0;
}
+static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev)
+{
+ mmc_detect_change(dev_get_drvdata(&pdev->dev), msecs_to_jiffies(100));
+}
+
+static const struct sh_mobile_sdhi_ops sdhi_ops = {
+ .cd_wakeup = sh_mobile_sdhi_cd_wakeup,
+};
+
static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
{
struct sh_mobile_sdhi *priv;
@@ -109,6 +118,12 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data = &priv->mmc_data;
p->pdata = mmc_data;
+ if (p->init) {
+ ret = p->init(pdev, &sdhi_ops);
+ if (ret)
+ goto einit;
+ }
+
snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id);
priv->clk = clk_get(&pdev->dev, clk_name);
if (IS_ERR(priv->clk)) {
@@ -117,8 +132,6 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
goto eclkget;
}
- clk_enable(priv->clk);
-
mmc_data->hclk = clk_get_rate(priv->clk);
mmc_data->set_pwr = sh_mobile_sdhi_set_pwr;
mmc_data->get_cd = sh_mobile_sdhi_get_cd;
@@ -129,6 +142,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
mmc_data->ocr_mask = p->tmio_ocr_mask;
mmc_data->capabilities |= p->tmio_caps;
+ mmc_data->cd_gpio = p->cd_gpio;
if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) {
priv->param_tx.slave_id = p->dma_slave_tx;
@@ -211,7 +225,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
mmc_hostname(host->mmc), (unsigned long)
- (platform_get_resource(pdev,IORESOURCE_MEM, 0)->start),
+ (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
mmc_data->hclk / 1000000);
return ret;
@@ -232,9 +246,11 @@ eirq_sdio:
eirq_card_detect:
tmio_mmc_host_remove(host);
eprobe:
- clk_disable(priv->clk);
clk_put(priv->clk);
eclkget:
+ if (p->cleanup)
+ p->cleanup(pdev);
+einit:
kfree(priv);
return ret;
}
@@ -258,8 +274,11 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
free_irq(irq, host);
}
- clk_disable(priv->clk);
clk_put(priv->clk);
+
+ if (p->cleanup)
+ p->cleanup(pdev);
+
kfree(priv);
return 0;
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index f96c536..d857f5c 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -47,16 +47,14 @@ struct tmio_mmc_host {
struct mmc_request *mrq;
struct mmc_data *data;
struct mmc_host *mmc;
- unsigned int sdio_irq_enabled;
+
+ /* Controller power state */
+ bool power;
/* Callbacks for clock / power control */
void (*set_pwr)(struct platform_device *host, int state);
void (*set_clk_div)(struct platform_device *host, int state);
- int pm_error;
- /* recognise system-wide suspend in runtime PM methods */
- bool pm_global;
-
/* pio related stuff */
struct scatterlist *sg_ptr;
struct scatterlist *sg_orig;
@@ -86,6 +84,7 @@ struct tmio_mmc_host {
spinlock_t lock; /* protect host private data */
unsigned long last_req_ts;
struct mutex ios_lock; /* protect set_ios() context */
+ bool native_hotplug;
};
int tmio_mmc_host_probe(struct tmio_mmc_host **host,
diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c
index 8253ec1..fff9286 100644
--- a/drivers/mmc/host/tmio_mmc_dma.c
+++ b/drivers/mmc/host/tmio_mmc_dma.c
@@ -88,7 +88,7 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host)
ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_FROM_DEVICE);
if (ret > 0)
- desc = chan->device->device_prep_slave_sg(chan, sg, ret,
+ desc = dmaengine_prep_slave_sg(chan, sg, ret,
DMA_DEV_TO_MEM, DMA_CTRL_ACK);
if (desc) {
@@ -169,7 +169,7 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host)
ret = dma_map_sg(chan->device->dev, sg, host->sg_len, DMA_TO_DEVICE);
if (ret > 0)
- desc = chan->device->device_prep_slave_sg(chan, sg, ret,
+ desc = dmaengine_prep_slave_sg(chan, sg, ret,
DMA_MEM_TO_DEV, DMA_CTRL_ACK);
if (desc) {
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index e219889..9a7996a 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -34,6 +34,7 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/mfd/tmio.h>
+#include <linux/mmc/cd-gpio.h>
#include <linux/mmc/host.h>
#include <linux/mmc/tmio.h>
#include <linux/module.h>
@@ -127,7 +128,6 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
struct tmio_mmc_host *host = mmc_priv(mmc);
if (enable) {
- host->sdio_irq_enabled = 1;
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL &
~TMIO_SDIO_STAT_IOIRQ;
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
@@ -136,7 +136,6 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
- host->sdio_irq_enabled = 0;
}
}
@@ -304,6 +303,7 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
{
struct mmc_data *data = host->data;
int c = cmd->opcode;
+ u32 irq_mask = TMIO_MASK_CMD;
/* Command 12 is handled by hardware */
if (cmd->opcode == 12 && !cmd->arg) {
@@ -339,7 +339,9 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
c |= TRANSFER_READ;
}
- tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_CMD);
+ if (!host->native_hotplug)
+ irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
+ tmio_mmc_enable_mmc_irqs(host, irq_mask);
/* Fire off the command */
sd_ctrl_write32(host, CTL_ARG_REG, cmd->arg);
@@ -758,7 +760,7 @@ fail:
static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct tmio_mmc_host *host = mmc_priv(mmc);
- struct tmio_mmc_data *pdata = host->pdata;
+ struct device *dev = &host->pdev->dev;
unsigned long flags;
mutex_lock(&host->ios_lock);
@@ -766,13 +768,13 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_lock_irqsave(&host->lock, flags);
if (host->mrq) {
if (IS_ERR(host->mrq)) {
- dev_dbg(&host->pdev->dev,
+ dev_dbg(dev,
"%s.%d: concurrent .set_ios(), clk %u, mode %u\n",
current->comm, task_pid_nr(current),
ios->clock, ios->power_mode);
host->mrq = ERR_PTR(-EINTR);
} else {
- dev_dbg(&host->pdev->dev,
+ dev_dbg(dev,
"%s.%d: CMD%u active since %lu, now %lu!\n",
current->comm, task_pid_nr(current),
host->mrq->cmd->opcode, host->last_req_ts, jiffies);
@@ -788,13 +790,15 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_unlock_irqrestore(&host->lock, flags);
/*
- * pdata->power == false only if COLD_CD is available, otherwise only
- * in short time intervals during probing or resuming
+ * host->power toggles between false and true in both cases - either
+ * or not the controller can be runtime-suspended during inactivity.
+ * But if the controller has to be kept on, the runtime-pm usage_count
+ * is kept positive, so no suspending actually takes place.
*/
if (ios->power_mode == MMC_POWER_ON && ios->clock) {
- if (!pdata->power) {
- pm_runtime_get_sync(&host->pdev->dev);
- pdata->power = true;
+ if (!host->power) {
+ pm_runtime_get_sync(dev);
+ host->power = true;
}
tmio_mmc_set_clock(host, ios->clock);
/* power up SD bus */
@@ -805,9 +809,9 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
} else if (ios->power_mode != MMC_POWER_UP) {
if (host->set_pwr && ios->power_mode == MMC_POWER_OFF)
host->set_pwr(host->pdev, 0);
- if (pdata->power) {
- pdata->power = false;
- pm_runtime_put(&host->pdev->dev);
+ if (host->power) {
+ host->power = false;
+ pm_runtime_put(dev);
}
tmio_mmc_clk_stop(host);
}
@@ -913,7 +917,11 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
else
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- pdata->power = false;
+ _host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
+ mmc->caps & MMC_CAP_NEEDS_POLL ||
+ mmc->caps & MMC_CAP_NONREMOVABLE);
+
+ _host->power = false;
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume(&pdev->dev);
if (ret < 0)
@@ -926,14 +934,13 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
* 3) a worker thread polls the sdhi - indicated by MMC_CAP_NEEDS_POLL
* 4) the medium is non-removable - indicated by MMC_CAP_NONREMOVABLE
*
- * While we increment the rtpm counter for all scenarios when the mmc
- * core activates us by calling an appropriate set_ios(), we must
+ * While we increment the runtime PM counter for all scenarios when
+ * the mmc core activates us by calling an appropriate set_ios(), we
+ * must additionally ensure that in case 2) the tmio mmc hardware stays
* additionally ensure that in case 2) the tmio mmc hardware stays
* powered on during runtime for the card detection to work.
*/
- if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD
- || mmc->caps & MMC_CAP_NEEDS_POLL
- || mmc->caps & MMC_CAP_NONREMOVABLE))
+ if (_host->native_hotplug)
pm_runtime_get_noresume(&pdev->dev);
tmio_mmc_clk_stop(_host);
@@ -963,9 +970,19 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
irq_mask |= TMIO_MASK_READOP;
if (!_host->chan_tx)
irq_mask |= TMIO_MASK_WRITEOP;
+ if (!_host->native_hotplug)
+ irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
tmio_mmc_enable_mmc_irqs(_host, irq_mask);
+ if (pdata->flags & TMIO_MMC_USE_GPIO_CD) {
+ ret = mmc_cd_gpio_request(mmc, pdata->cd_gpio);
+ if (ret < 0) {
+ tmio_mmc_host_remove(_host);
+ return ret;
+ }
+ }
+
*host = _host;
return 0;
@@ -983,22 +1000,22 @@ EXPORT_SYMBOL(tmio_mmc_host_probe);
void tmio_mmc_host_remove(struct tmio_mmc_host *host)
{
struct platform_device *pdev = host->pdev;
+ struct tmio_mmc_data *pdata = host->pdata;
+ struct mmc_host *mmc = host->mmc;
- /*
- * We don't have to manipulate pdata->power here: if there is a card in
- * the slot, the runtime PM is active and our .runtime_resume() will not
- * be run. If there is no card in the slot and the platform can suspend
- * the controller, the runtime PM is suspended and pdata->power == false,
- * so, our .runtime_resume() will not try to detect a card in the slot.
- */
- if (host->pdata->flags & TMIO_MMC_HAS_COLD_CD
- || host->mmc->caps & MMC_CAP_NEEDS_POLL
- || host->mmc->caps & MMC_CAP_NONREMOVABLE)
+ if (pdata->flags & TMIO_MMC_USE_GPIO_CD)
+ /*
+ * This means we can miss a card-eject, but this is anyway
+ * possible, because of delayed processing of hotplug events.
+ */
+ mmc_cd_gpio_free(mmc);
+
+ if (!host->native_hotplug)
pm_runtime_get_sync(&pdev->dev);
dev_pm_qos_hide_latency_limit(&pdev->dev);
- mmc_remove_host(host->mmc);
+ mmc_remove_host(mmc);
cancel_work_sync(&host->done);
cancel_delayed_work_sync(&host->delayed_reset_work);
tmio_mmc_release_dma(host);
@@ -1007,7 +1024,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
pm_runtime_disable(&pdev->dev);
iounmap(host->ctl);
- mmc_free_host(host->mmc);
+ mmc_free_host(mmc);
}
EXPORT_SYMBOL(tmio_mmc_host_remove);
@@ -1021,8 +1038,6 @@ int tmio_mmc_host_suspend(struct device *dev)
if (!ret)
tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
- host->pm_error = pm_runtime_put_sync(dev);
-
return ret;
}
EXPORT_SYMBOL(tmio_mmc_host_suspend);
@@ -1032,20 +1047,10 @@ int tmio_mmc_host_resume(struct device *dev)
struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc);
- /* The MMC core will perform the complete set up */
- host->pdata->power = false;
-
- host->pm_global = true;
- if (!host->pm_error)
- pm_runtime_get_sync(dev);
-
- if (host->pm_global) {
- /* Runtime PM resume callback didn't run */
- tmio_mmc_reset(host);
- tmio_mmc_enable_dma(host, true);
- host->pm_global = false;
- }
+ tmio_mmc_reset(host);
+ tmio_mmc_enable_dma(host, true);
+ /* The MMC core will perform the complete set up */
return mmc_resume_host(mmc);
}
EXPORT_SYMBOL(tmio_mmc_host_resume);
@@ -1062,19 +1067,10 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc);
- struct tmio_mmc_data *pdata = host->pdata;
tmio_mmc_reset(host);
tmio_mmc_enable_dma(host, true);
- if (pdata->power) {
- /* Only entered after a card-insert interrupt */
- if (!mmc->card)
- tmio_mmc_set_ios(mmc, &mmc->ios);
- mmc_detect_change(mmc, msecs_to_jiffies(100));
- }
- host->pm_global = false;
-
return 0;
}
EXPORT_SYMBOL(tmio_mmc_host_runtime_resume);
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 1be6218..5760c1a 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -1,6 +1,6 @@
menuconfig MTD
tristate "Memory Technology Device (MTD) support"
- depends on HAS_IOMEM
+ depends on GENERIC_IO
help
Memory Technology Devices are flash, RAM and similar chips, often
used for solid state file systems on embedded devices. This option
@@ -304,9 +304,6 @@ config MTD_OOPS
buffer in a flash partition where it can be read back at some
later point.
- To use, add console=ttyMTDx to the kernel command line,
- where x is the MTD device number to use.
-
config MTD_SWAP
tristate "Swap on MTD device support"
depends on MTD && SWAP
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index 9bcd1f4..dbbd2ed 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -87,7 +87,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *, struct cfi_private **
static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys);
-static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len);
+static int cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len);
static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
@@ -262,9 +262,9 @@ static void fixup_st_m28w320cb(struct mtd_info *mtd)
static void fixup_use_point(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
- if (!mtd->point && map_is_linear(map)) {
- mtd->point = cfi_intelext_point;
- mtd->unpoint = cfi_intelext_unpoint;
+ if (!mtd->_point && map_is_linear(map)) {
+ mtd->_point = cfi_intelext_point;
+ mtd->_unpoint = cfi_intelext_unpoint;
}
}
@@ -274,8 +274,8 @@ static void fixup_use_write_buffers(struct mtd_info *mtd)
struct cfi_private *cfi = map->fldrv_priv;
if (cfi->cfiq->BufWriteTimeoutTyp) {
printk(KERN_INFO "Using buffer write method\n" );
- mtd->write = cfi_intelext_write_buffers;
- mtd->writev = cfi_intelext_writev;
+ mtd->_write = cfi_intelext_write_buffers;
+ mtd->_writev = cfi_intelext_writev;
}
}
@@ -443,15 +443,15 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
mtd->type = MTD_NORFLASH;
/* Fill in the default mtd operations */
- mtd->erase = cfi_intelext_erase_varsize;
- mtd->read = cfi_intelext_read;
- mtd->write = cfi_intelext_write_words;
- mtd->sync = cfi_intelext_sync;
- mtd->lock = cfi_intelext_lock;
- mtd->unlock = cfi_intelext_unlock;
- mtd->is_locked = cfi_intelext_is_locked;
- mtd->suspend = cfi_intelext_suspend;
- mtd->resume = cfi_intelext_resume;
+ mtd->_erase = cfi_intelext_erase_varsize;
+ mtd->_read = cfi_intelext_read;
+ mtd->_write = cfi_intelext_write_words;
+ mtd->_sync = cfi_intelext_sync;
+ mtd->_lock = cfi_intelext_lock;
+ mtd->_unlock = cfi_intelext_unlock;
+ mtd->_is_locked = cfi_intelext_is_locked;
+ mtd->_suspend = cfi_intelext_suspend;
+ mtd->_resume = cfi_intelext_resume;
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
mtd->writesize = 1;
@@ -600,12 +600,12 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
}
#ifdef CONFIG_MTD_OTP
- mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
- mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
- mtd->write_user_prot_reg = cfi_intelext_write_user_prot_reg;
- mtd->lock_user_prot_reg = cfi_intelext_lock_user_prot_reg;
- mtd->get_fact_prot_info = cfi_intelext_get_fact_prot_info;
- mtd->get_user_prot_info = cfi_intelext_get_user_prot_info;
+ mtd->_read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
+ mtd->_read_user_prot_reg = cfi_intelext_read_user_prot_reg;
+ mtd->_write_user_prot_reg = cfi_intelext_write_user_prot_reg;
+ mtd->_lock_user_prot_reg = cfi_intelext_lock_user_prot_reg;
+ mtd->_get_fact_prot_info = cfi_intelext_get_fact_prot_info;
+ mtd->_get_user_prot_info = cfi_intelext_get_user_prot_info;
#endif
/* This function has the potential to distort the reality
@@ -1017,8 +1017,6 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
case FL_READY:
case FL_STATUS:
case FL_JEDEC_QUERY:
- /* We should really make set_vpp() count, rather than doing this */
- DISABLE_VPP(map);
break;
default:
printk(KERN_ERR "%s: put_chip() called with oldstate %d!!\n", map->name, chip->oldstate);
@@ -1324,7 +1322,7 @@ static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len,
int chipnum;
int ret = 0;
- if (!map->virt || (from + len > mtd->size))
+ if (!map->virt)
return -EINVAL;
/* Now lock the chip(s) to POINT state */
@@ -1334,7 +1332,6 @@ static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len,
ofs = from - (chipnum << cfi->chipshift);
*virt = map->virt + cfi->chips[chipnum].start + ofs;
- *retlen = 0;
if (phys)
*phys = map->phys + cfi->chips[chipnum].start + ofs;
@@ -1369,12 +1366,12 @@ static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len,
return 0;
}
-static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+static int cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
unsigned long ofs;
- int chipnum;
+ int chipnum, err = 0;
/* Now unlock the chip(s) POINT state */
@@ -1382,7 +1379,7 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
chipnum = (from >> cfi->chipshift);
ofs = from - (chipnum << cfi->chipshift);
- while (len) {
+ while (len && !err) {
unsigned long thislen;
struct flchip *chip;
@@ -1400,8 +1397,10 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
chip->ref_point_counter--;
if(chip->ref_point_counter == 0)
chip->state = FL_READY;
- } else
- printk(KERN_ERR "%s: Warning: unpoint called on non pointed region\n", map->name); /* Should this give an error? */
+ } else {
+ printk(KERN_ERR "%s: Error: unpoint called on non pointed region\n", map->name);
+ err = -EINVAL;
+ }
put_chip(map, chip, chip->start);
mutex_unlock(&chip->mutex);
@@ -1410,6 +1409,8 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
ofs = 0;
chipnum++;
}
+
+ return err;
}
static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
@@ -1456,8 +1457,6 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz
chipnum = (from >> cfi->chipshift);
ofs = from - (chipnum << cfi->chipshift);
- *retlen = 0;
-
while (len) {
unsigned long thislen;
@@ -1551,7 +1550,8 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
}
xip_enable(map, chip, adr);
- out: put_chip(map, chip, adr);
+ out: DISABLE_VPP(map);
+ put_chip(map, chip, adr);
mutex_unlock(&chip->mutex);
return ret;
}
@@ -1565,10 +1565,6 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
int chipnum;
unsigned long ofs;
- *retlen = 0;
- if (!len)
- return 0;
-
chipnum = to >> cfi->chipshift;
ofs = to - (chipnum << cfi->chipshift);
@@ -1794,7 +1790,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
}
xip_enable(map, chip, cmd_adr);
- out: put_chip(map, chip, cmd_adr);
+ out: DISABLE_VPP(map);
+ put_chip(map, chip, cmd_adr);
mutex_unlock(&chip->mutex);
return ret;
}
@@ -1813,7 +1810,6 @@ static int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs,
for (i = 0; i < count; i++)
len += vecs[i].iov_len;
- *retlen = 0;
if (!len)
return 0;
@@ -1932,6 +1928,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
ret = -EIO;
} else if (chipstatus & 0x20 && retries--) {
printk(KERN_DEBUG "block erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus);
+ DISABLE_VPP(map);
put_chip(map, chip, adr);
mutex_unlock(&chip->mutex);
goto retry;
@@ -1944,7 +1941,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
}
xip_enable(map, chip, adr);
- out: put_chip(map, chip, adr);
+ out: DISABLE_VPP(map);
+ put_chip(map, chip, adr);
mutex_unlock(&chip->mutex);
return ret;
}
@@ -2086,7 +2084,8 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
}
xip_enable(map, chip, adr);
-out: put_chip(map, chip, adr);
+ out: DISABLE_VPP(map);
+ put_chip(map, chip, adr);
mutex_unlock(&chip->mutex);
return ret;
}
@@ -2483,7 +2482,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd)
allowed to. Or should we return -EAGAIN, because the upper layers
ought to have already shut down anything which was using the device
anyway? The latter for now. */
- printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->oldstate);
+ printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->state);
ret = -EAGAIN;
case FL_PM_SUSPENDED:
break;
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index 8d70895..d02592e 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -59,6 +59,9 @@ static void cfi_amdstd_resume (struct mtd_info *);
static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *);
static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf);
+
static void cfi_amdstd_destroy(struct mtd_info *);
struct mtd_info *cfi_cmdset_0002(struct map_info *, int);
@@ -189,7 +192,7 @@ static void fixup_use_write_buffers(struct mtd_info *mtd)
struct cfi_private *cfi = map->fldrv_priv;
if (cfi->cfiq->BufWriteTimeoutTyp) {
pr_debug("Using buffer write method\n" );
- mtd->write = cfi_amdstd_write_buffers;
+ mtd->_write = cfi_amdstd_write_buffers;
}
}
@@ -228,8 +231,8 @@ static void fixup_convert_atmel_pri(struct mtd_info *mtd)
static void fixup_use_secsi(struct mtd_info *mtd)
{
/* Setup for chips with a secsi area */
- mtd->read_user_prot_reg = cfi_amdstd_secsi_read;
- mtd->read_fact_prot_reg = cfi_amdstd_secsi_read;
+ mtd->_read_user_prot_reg = cfi_amdstd_secsi_read;
+ mtd->_read_fact_prot_reg = cfi_amdstd_secsi_read;
}
static void fixup_use_erase_chip(struct mtd_info *mtd)
@@ -238,7 +241,7 @@ static void fixup_use_erase_chip(struct mtd_info *mtd)
struct cfi_private *cfi = map->fldrv_priv;
if ((cfi->cfiq->NumEraseRegions == 1) &&
((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) {
- mtd->erase = cfi_amdstd_erase_chip;
+ mtd->_erase = cfi_amdstd_erase_chip;
}
}
@@ -249,8 +252,8 @@ static void fixup_use_erase_chip(struct mtd_info *mtd)
*/
static void fixup_use_atmel_lock(struct mtd_info *mtd)
{
- mtd->lock = cfi_atmel_lock;
- mtd->unlock = cfi_atmel_unlock;
+ mtd->_lock = cfi_atmel_lock;
+ mtd->_unlock = cfi_atmel_unlock;
mtd->flags |= MTD_POWERUP_LOCK;
}
@@ -429,12 +432,12 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
mtd->type = MTD_NORFLASH;
/* Fill in the default mtd operations */
- mtd->erase = cfi_amdstd_erase_varsize;
- mtd->write = cfi_amdstd_write_words;
- mtd->read = cfi_amdstd_read;
- mtd->sync = cfi_amdstd_sync;
- mtd->suspend = cfi_amdstd_suspend;
- mtd->resume = cfi_amdstd_resume;
+ mtd->_erase = cfi_amdstd_erase_varsize;
+ mtd->_write = cfi_amdstd_write_words;
+ mtd->_read = cfi_amdstd_read;
+ mtd->_sync = cfi_amdstd_sync;
+ mtd->_suspend = cfi_amdstd_suspend;
+ mtd->_resume = cfi_amdstd_resume;
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
mtd->writesize = 1;
@@ -443,6 +446,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
pr_debug("MTD %s(): write buffer size %d\n", __func__,
mtd->writebufsize);
+ mtd->_panic_write = cfi_amdstd_panic_write;
mtd->reboot_notifier.notifier_call = cfi_amdstd_reboot;
if (cfi->cfi_mode==CFI_MODE_CFI){
@@ -770,8 +774,6 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
case FL_READY:
case FL_STATUS:
- /* We should really make set_vpp() count, rather than doing this */
- DISABLE_VPP(map);
break;
default:
printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate);
@@ -1013,13 +1015,9 @@ static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_
int ret = 0;
/* ofs: offset within the first chip that the first read should start */
-
chipnum = (from >> cfi->chipshift);
ofs = from - (chipnum << cfi->chipshift);
-
- *retlen = 0;
-
while (len) {
unsigned long thislen;
@@ -1097,16 +1095,11 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len,
int chipnum;
int ret = 0;
-
/* ofs: offset within the first chip that the first read should start */
-
/* 8 secsi bytes per chip */
chipnum=from>>3;
ofs=from & 7;
-
- *retlen = 0;
-
while (len) {
unsigned long thislen;
@@ -1234,6 +1227,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
xip_enable(map, chip, adr);
op_done:
chip->state = FL_READY;
+ DISABLE_VPP(map);
put_chip(map, chip, adr);
mutex_unlock(&chip->mutex);
@@ -1251,10 +1245,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
unsigned long ofs, chipstart;
DECLARE_WAITQUEUE(wait, current);
- *retlen = 0;
- if (!len)
- return 0;
-
chipnum = to >> cfi->chipshift;
ofs = to - (chipnum << cfi->chipshift);
chipstart = cfi->chips[chipnum].start;
@@ -1476,6 +1466,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
ret = -EIO;
op_done:
chip->state = FL_READY;
+ DISABLE_VPP(map);
put_chip(map, chip, adr);
mutex_unlock(&chip->mutex);
@@ -1493,10 +1484,6 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
int chipnum;
unsigned long ofs;
- *retlen = 0;
- if (!len)
- return 0;
-
chipnum = to >> cfi->chipshift;
ofs = to - (chipnum << cfi->chipshift);
@@ -1562,6 +1549,238 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
return 0;
}
+/*
+ * Wait for the flash chip to become ready to write data
+ *
+ * This is only called during the panic_write() path. When panic_write()
+ * is called, the kernel is in the process of a panic, and will soon be
+ * dead. Therefore we don't take any locks, and attempt to get access
+ * to the chip as soon as possible.
+ */
+static int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip,
+ unsigned long adr)
+{
+ struct cfi_private *cfi = map->fldrv_priv;
+ int retries = 10;
+ int i;
+
+ /*
+ * If the driver thinks the chip is idle, and no toggle bits
+ * are changing, then the chip is actually idle for sure.
+ */
+ if (chip->state == FL_READY && chip_ready(map, adr))
+ return 0;
+
+ /*
+ * Try several times to reset the chip and then wait for it
+ * to become idle. The upper limit of a few milliseconds of
+ * delay isn't a big problem: the kernel is dying anyway. It
+ * is more important to save the messages.
+ */
+ while (retries > 0) {
+ const unsigned long timeo = (HZ / 1000) + 1;
+
+ /* send the reset command */
+ map_write(map, CMD(0xF0), chip->start);
+
+ /* wait for the chip to become ready */
+ for (i = 0; i < jiffies_to_usecs(timeo); i++) {
+ if (chip_ready(map, adr))
+ return 0;
+
+ udelay(1);
+ }
+ }
+
+ /* the chip never became ready */
+ return -EBUSY;
+}
+
+/*
+ * Write out one word of data to a single flash chip during a kernel panic
+ *
+ * This is only called during the panic_write() path. When panic_write()
+ * is called, the kernel is in the process of a panic, and will soon be
+ * dead. Therefore we don't take any locks, and attempt to get access
+ * to the chip as soon as possible.
+ *
+ * The implementation of this routine is intentionally similar to
+ * do_write_oneword(), in order to ease code maintenance.
+ */
+static int do_panic_write_oneword(struct map_info *map, struct flchip *chip,
+ unsigned long adr, map_word datum)
+{
+ const unsigned long uWriteTimeout = (HZ / 1000) + 1;
+ struct cfi_private *cfi = map->fldrv_priv;
+ int retry_cnt = 0;
+ map_word oldd;
+ int ret = 0;
+ int i;
+
+ adr += chip->start;
+
+ ret = cfi_amdstd_panic_wait(map, chip, adr);
+ if (ret)
+ return ret;
+
+ pr_debug("MTD %s(): PANIC WRITE 0x%.8lx(0x%.8lx)\n",
+ __func__, adr, datum.x[0]);
+
+ /*
+ * Check for a NOP for the case when the datum to write is already
+ * present - it saves time and works around buggy chips that corrupt
+ * data at other locations when 0xff is written to a location that
+ * already contains 0xff.
+ */
+ oldd = map_read(map, adr);
+ if (map_word_equal(map, oldd, datum)) {
+ pr_debug("MTD %s(): NOP\n", __func__);
+ goto op_done;
+ }
+
+ ENABLE_VPP(map);
+
+retry:
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+ cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+ map_write(map, datum, adr);
+
+ for (i = 0; i < jiffies_to_usecs(uWriteTimeout); i++) {
+ if (chip_ready(map, adr))
+ break;
+
+ udelay(1);
+ }
+
+ if (!chip_good(map, adr, datum)) {
+ /* reset on all failures. */
+ map_write(map, CMD(0xF0), chip->start);
+ /* FIXME - should have reset delay before continuing */
+
+ if (++retry_cnt <= MAX_WORD_RETRIES)
+ goto retry;
+
+ ret = -EIO;
+ }
+
+op_done:
+ DISABLE_VPP(map);
+ return ret;
+}
+
+/*
+ * Write out some data during a kernel panic
+ *
+ * This is used by the mtdoops driver to save the dying messages from a
+ * kernel which has panic'd.
+ *
+ * This routine ignores all of the locking used throughout the rest of the
+ * driver, in order to ensure that the data gets written out no matter what
+ * state this driver (and the flash chip itself) was in when the kernel crashed.
+ *
+ * The implementation of this routine is intentionally similar to
+ * cfi_amdstd_write_words(), in order to ease code maintenance.
+ */
+static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+ unsigned long ofs, chipstart;
+ int ret = 0;
+ int chipnum;
+
+ chipnum = to >> cfi->chipshift;
+ ofs = to - (chipnum << cfi->chipshift);
+ chipstart = cfi->chips[chipnum].start;
+
+ /* If it's not bus aligned, do the first byte write */
+ if (ofs & (map_bankwidth(map) - 1)) {
+ unsigned long bus_ofs = ofs & ~(map_bankwidth(map) - 1);
+ int i = ofs - bus_ofs;
+ int n = 0;
+ map_word tmp_buf;
+
+ ret = cfi_amdstd_panic_wait(map, &cfi->chips[chipnum], bus_ofs);
+ if (ret)
+ return ret;
+
+ /* Load 'tmp_buf' with old contents of flash */
+ tmp_buf = map_read(map, bus_ofs + chipstart);
+
+ /* Number of bytes to copy from buffer */
+ n = min_t(int, len, map_bankwidth(map) - i);
+
+ tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n);
+
+ ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
+ bus_ofs, tmp_buf);
+ if (ret)
+ return ret;
+
+ ofs += n;
+ buf += n;
+ (*retlen) += n;
+ len -= n;
+
+ if (ofs >> cfi->chipshift) {
+ chipnum++;
+ ofs = 0;
+ if (chipnum == cfi->numchips)
+ return 0;
+ }
+ }
+
+ /* We are now aligned, write as much as possible */
+ while (len >= map_bankwidth(map)) {
+ map_word datum;
+
+ datum = map_word_load(map, buf);
+
+ ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
+ ofs, datum);
+ if (ret)
+ return ret;
+
+ ofs += map_bankwidth(map);
+ buf += map_bankwidth(map);
+ (*retlen) += map_bankwidth(map);
+ len -= map_bankwidth(map);
+
+ if (ofs >> cfi->chipshift) {
+ chipnum++;
+ ofs = 0;
+ if (chipnum == cfi->numchips)
+ return 0;
+
+ chipstart = cfi->chips[chipnum].start;
+ }
+ }
+
+ /* Write the trailing bytes if any */
+ if (len & (map_bankwidth(map) - 1)) {
+ map_word tmp_buf;
+
+ ret = cfi_amdstd_panic_wait(map, &cfi->chips[chipnum], ofs);
+ if (ret)
+ return ret;
+
+ tmp_buf = map_read(map, ofs + chipstart);
+
+ tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
+
+ ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
+ ofs, tmp_buf);
+ if (ret)
+ return ret;
+
+ (*retlen) += len;
+ }
+
+ return 0;
+}
+
/*
* Handle devices with one erase region, that only implement
@@ -1649,6 +1868,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
chip->state = FL_READY;
xip_enable(map, chip, adr);
+ DISABLE_VPP(map);
put_chip(map, chip, adr);
mutex_unlock(&chip->mutex);
@@ -1739,6 +1959,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
}
chip->state = FL_READY;
+ DISABLE_VPP(map);
put_chip(map, chip, adr);
mutex_unlock(&chip->mutex);
return ret;
diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c
index 85e8018..096993f 100644
--- a/drivers/mtd/chips/cfi_cmdset_0020.c
+++ b/drivers/mtd/chips/cfi_cmdset_0020.c
@@ -228,15 +228,15 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
}
/* Also select the correct geometry setup too */
- mtd->erase = cfi_staa_erase_varsize;
- mtd->read = cfi_staa_read;
- mtd->write = cfi_staa_write_buffers;
- mtd->writev = cfi_staa_writev;
- mtd->sync = cfi_staa_sync;
- mtd->lock = cfi_staa_lock;
- mtd->unlock = cfi_staa_unlock;
- mtd->suspend = cfi_staa_suspend;
- mtd->resume = cfi_staa_resume;
+ mtd->_erase = cfi_staa_erase_varsize;
+ mtd->_read = cfi_staa_read;
+ mtd->_write = cfi_staa_write_buffers;
+ mtd->_writev = cfi_staa_writev;
+ mtd->_sync = cfi_staa_sync;
+ mtd->_lock = cfi_staa_lock;
+ mtd->_unlock = cfi_staa_unlock;
+ mtd->_suspend = cfi_staa_suspend;
+ mtd->_resume = cfi_staa_resume;
mtd->flags = MTD_CAP_NORFLASH & ~MTD_BIT_WRITEABLE;
mtd->writesize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
@@ -394,8 +394,6 @@ static int cfi_staa_read (struct mtd_info *mtd, loff_t from, size_t len, size_t
chipnum = (from >> cfi->chipshift);
ofs = from - (chipnum << cfi->chipshift);
- *retlen = 0;
-
while (len) {
unsigned long thislen;
@@ -617,10 +615,6 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to,
int chipnum;
unsigned long ofs;
- *retlen = 0;
- if (!len)
- return 0;
-
chipnum = to >> cfi->chipshift;
ofs = to - (chipnum << cfi->chipshift);
@@ -904,12 +898,6 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd,
int i, first;
struct mtd_erase_region_info *regions = mtd->eraseregions;
- if (instr->addr > mtd->size)
- return -EINVAL;
-
- if ((instr->len + instr->addr) > mtd->size)
- return -EINVAL;
-
/* Check that both start and end of the requested erase are
* aligned with the erasesize at the appropriate addresses.
*/
@@ -1155,9 +1143,6 @@ static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
if (len & (mtd->erasesize -1))
return -EINVAL;
- if ((len + ofs) > mtd->size)
- return -EINVAL;
-
chipnum = ofs >> cfi->chipshift;
adr = ofs - (chipnum << cfi->chipshift);
diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c
index 8e46405..f992418 100644
--- a/drivers/mtd/chips/cfi_util.c
+++ b/drivers/mtd/chips/cfi_util.c
@@ -173,12 +173,6 @@ int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
int i, first;
struct mtd_erase_region_info *regions = mtd->eraseregions;
- if (ofs > mtd->size)
- return -EINVAL;
-
- if ((len + ofs) > mtd->size)
- return -EINVAL;
-
/* Check that both start and end of the requested erase are
* aligned with the erasesize at the appropriate addresses.
*/
diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h
index 89c6595..800b0e8 100644
--- a/drivers/mtd/chips/fwh_lock.h
+++ b/drivers/mtd/chips/fwh_lock.h
@@ -101,7 +101,7 @@ static void fixup_use_fwh_lock(struct mtd_info *mtd)
{
printk(KERN_NOTICE "using fwh lock/unlock method\n");
/* Setup for the chips with the fwh lock method */
- mtd->lock = fwh_lock_varsize;
- mtd->unlock = fwh_unlock_varsize;
+ mtd->_lock = fwh_lock_varsize;
+ mtd->_unlock = fwh_unlock_varsize;
}
#endif /* FWH_LOCK_H */
diff --git a/drivers/mtd/chips/map_absent.c b/drivers/mtd/chips/map_absent.c
index f2b8729..f7a5bca 100644
--- a/drivers/mtd/chips/map_absent.c
+++ b/drivers/mtd/chips/map_absent.c
@@ -55,10 +55,10 @@ static struct mtd_info *map_absent_probe(struct map_info *map)
mtd->name = map->name;
mtd->type = MTD_ABSENT;
mtd->size = map->size;
- mtd->erase = map_absent_erase;
- mtd->read = map_absent_read;
- mtd->write = map_absent_write;
- mtd->sync = map_absent_sync;
+ mtd->_erase = map_absent_erase;
+ mtd->_read = map_absent_read;
+ mtd->_write = map_absent_write;
+ mtd->_sync = map_absent_sync;
mtd->flags = 0;
mtd->erasesize = PAGE_SIZE;
mtd->writesize = 1;
@@ -70,13 +70,11 @@ static struct mtd_info *map_absent_probe(struct map_info *map)
static int map_absent_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
- *retlen = 0;
return -ENODEV;
}
static int map_absent_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
{
- *retlen = 0;
return -ENODEV;
}
diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c
index 67640cc..991c2a1 100644
--- a/drivers/mtd/chips/map_ram.c
+++ b/drivers/mtd/chips/map_ram.c
@@ -64,11 +64,11 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
mtd->name = map->name;
mtd->type = MTD_RAM;
mtd->size = map->size;
- mtd->erase = mapram_erase;
- mtd->get_unmapped_area = mapram_unmapped_area;
- mtd->read = mapram_read;
- mtd->write = mapram_write;
- mtd->sync = mapram_nop;
+ mtd->_erase = mapram_erase;
+ mtd->_get_unmapped_area = mapram_unmapped_area;
+ mtd->_read = mapram_read;
+ mtd->_write = mapram_write;
+ mtd->_sync = mapram_nop;
mtd->flags = MTD_CAP_RAM;
mtd->writesize = 1;
@@ -122,14 +122,10 @@ static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr)
unsigned long i;
allff = map_word_ff(map);
-
for (i=0; i<instr->len; i += map_bankwidth(map))
map_write(map, allff, instr->addr + i);
-
instr->state = MTD_ERASE_DONE;
-
mtd_erase_callback(instr);
-
return 0;
}
diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c
index 593f73d..47a43cf 100644
--- a/drivers/mtd/chips/map_rom.c
+++ b/drivers/mtd/chips/map_rom.c
@@ -41,11 +41,11 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
mtd->name = map->name;
mtd->type = MTD_ROM;
mtd->size = map->size;
- mtd->get_unmapped_area = maprom_unmapped_area;
- mtd->read = maprom_read;
- mtd->write = maprom_write;
- mtd->sync = maprom_nop;
- mtd->erase = maprom_erase;
+ mtd->_get_unmapped_area = maprom_unmapped_area;
+ mtd->_read = maprom_read;
+ mtd->_write = maprom_write;
+ mtd->_sync = maprom_nop;
+ mtd->_erase = maprom_erase;
mtd->flags = MTD_CAP_ROM;
mtd->erasesize = map->size;
mtd->writesize = 1;
@@ -85,8 +85,7 @@ static void maprom_nop(struct mtd_info *mtd)
static int maprom_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
{
- printk(KERN_NOTICE "maprom_write called\n");
- return -EIO;
+ return -EROFS;
}
static int maprom_erase (struct mtd_info *mtd, struct erase_info *info)
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 37b05c3..4cdb2af 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -1,5 +1,6 @@
menu "Self-contained MTD device drivers"
depends on MTD!=n
+ depends on HAS_IOMEM
config MTD_PMC551
tristate "Ramix PMC551 PCI Mezzanine RAM card support"
@@ -102,6 +103,13 @@ config M25PXX_USE_FAST_READ
help
This option enables FAST_READ access supported by ST M25Pxx.
+config MTD_SPEAR_SMI
+ tristate "SPEAR MTD NOR Support through SMI controller"
+ depends on PLAT_SPEAR
+ default y
+ help
+ This enable SNOR support on SPEAR platforms using SMI controller
+
config MTD_SST25L
tristate "Support SST25L (non JEDEC) SPI Flash chips"
depends on SPI_MASTER
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 56c7cd4..a4dd1d8 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o
+obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o
CFLAGS_docg3.o += -I$(src) \ No newline at end of file
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index e7e46d1..a4a80b7 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -104,14 +104,6 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
int offset = from & (PAGE_SIZE-1);
int cpylen;
- if (from > mtd->size)
- return -EINVAL;
- if (from + len > mtd->size)
- len = mtd->size - from;
-
- if (retlen)
- *retlen = 0;
-
while (len) {
if ((offset + len) > PAGE_SIZE)
cpylen = PAGE_SIZE - offset; // multiple pages
@@ -148,8 +140,6 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
int offset = to & ~PAGE_MASK; // page offset
int cpylen;
- if (retlen)
- *retlen = 0;
while (len) {
if ((offset+len) > PAGE_SIZE)
cpylen = PAGE_SIZE - offset; // multiple pages
@@ -188,13 +178,6 @@ static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
struct block2mtd_dev *dev = mtd->priv;
int err;
- if (!len)
- return 0;
- if (to >= mtd->size)
- return -ENOSPC;
- if (to + len > mtd->size)
- len = mtd->size - to;
-
mutex_lock(&dev->write_mutex);
err = _block2mtd_write(dev, buf, to, len, retlen);
mutex_unlock(&dev->write_mutex);
@@ -283,13 +266,14 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
dev->mtd.erasesize = erase_size;
dev->mtd.writesize = 1;
+ dev->mtd.writebufsize = PAGE_SIZE;
dev->mtd.type = MTD_RAM;
dev->mtd.flags = MTD_CAP_RAM;
- dev->mtd.erase = block2mtd_erase;
- dev->mtd.write = block2mtd_write;
- dev->mtd.writev = mtd_writev;
- dev->mtd.sync = block2mtd_sync;
- dev->mtd.read = block2mtd_read;
+ dev->mtd._erase = block2mtd_erase;
+ dev->mtd._write = block2mtd_write;
+ dev->mtd._writev = mtd_writev;
+ dev->mtd._sync = block2mtd_sync;
+ dev->mtd._read = block2mtd_read;
dev->mtd.priv = dev;
dev->mtd.owner = THIS_MODULE;
diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c
index b1cdf64..a4eb8b5 100644
--- a/drivers/mtd/devices/doc2000.c
+++ b/drivers/mtd/devices/doc2000.c
@@ -562,14 +562,15 @@ void DoC2k_init(struct mtd_info *mtd)
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
- mtd->writesize = 512;
+ mtd->writebufsize = mtd->writesize = 512;
mtd->oobsize = 16;
+ mtd->ecc_strength = 2;
mtd->owner = THIS_MODULE;
- mtd->erase = doc_erase;
- mtd->read = doc_read;
- mtd->write = doc_write;
- mtd->read_oob = doc_read_oob;
- mtd->write_oob = doc_write_oob;
+ mtd->_erase = doc_erase;
+ mtd->_read = doc_read;
+ mtd->_write = doc_write;
+ mtd->_read_oob = doc_read_oob;
+ mtd->_write_oob = doc_write_oob;
this->curfloor = -1;
this->curchip = -1;
mutex_init(&this->lock);
@@ -602,13 +603,7 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
int i, len256 = 0, ret=0;
size_t left = len;
- /* Don't allow read past end of device */
- if (from >= this->totlen)
- return -EINVAL;
-
mutex_lock(&this->lock);
-
- *retlen = 0;
while (left) {
len = left;
@@ -748,13 +743,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t left = len;
int status;
- /* Don't allow write past end of device */
- if (to >= this->totlen)
- return -EINVAL;
-
mutex_lock(&this->lock);
-
- *retlen = 0;
while (left) {
len = left;
diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c
index 7543b98..f692795 100644
--- a/drivers/mtd/devices/doc2001.c
+++ b/drivers/mtd/devices/doc2001.c
@@ -346,14 +346,15 @@ void DoCMil_init(struct mtd_info *mtd)
/* FIXME: erase size is not always 8KiB */
mtd->erasesize = 0x2000;
- mtd->writesize = 512;
+ mtd->writebufsize = mtd->writesize = 512;
mtd->oobsize = 16;
+ mtd->ecc_strength = 2;
mtd->owner = THIS_MODULE;
- mtd->erase = doc_erase;
- mtd->read = doc_read;
- mtd->write = doc_write;
- mtd->read_oob = doc_read_oob;
- mtd->write_oob = doc_write_oob;
+ mtd->_erase = doc_erase;
+ mtd->_read = doc_read;
+ mtd->_write = doc_write;
+ mtd->_read_oob = doc_read_oob;
+ mtd->_write_oob = doc_write_oob;
this->curfloor = -1;
this->curchip = -1;
@@ -383,10 +384,6 @@ static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
void __iomem *docptr = this->virtadr;
struct Nand *mychip = &this->chips[from >> (this->chipshift)];
- /* Don't allow read past end of device */
- if (from >= this->totlen)
- return -EINVAL;
-
/* Don't allow a single read to cross a 512-byte block boundary */
if (from + len > ((from | 0x1ff) + 1))
len = ((from | 0x1ff) + 1) - from;
@@ -494,10 +491,6 @@ static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
void __iomem *docptr = this->virtadr;
struct Nand *mychip = &this->chips[to >> (this->chipshift)];
- /* Don't allow write past end of device */
- if (to >= this->totlen)
- return -EINVAL;
-
#if 0
/* Don't allow a single write to cross a 512-byte block boundary */
if (to + len > ( (to | 0x1ff) + 1))
@@ -599,7 +592,6 @@ static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
printk("Error programming flash\n");
/* Error in programming
FIXME: implement Bad Block Replacement (in nftl.c ??) */
- *retlen = 0;
ret = -EIO;
}
dummy = ReadDOC(docptr, LastDataRead);
diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c
index 177510d..04eb2e4 100644
--- a/drivers/mtd/devices/doc2001plus.c
+++ b/drivers/mtd/devices/doc2001plus.c
@@ -467,14 +467,15 @@ void DoCMilPlus_init(struct mtd_info *mtd)
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
- mtd->writesize = 512;
+ mtd->writebufsize = mtd->writesize = 512;
mtd->oobsize = 16;
+ mtd->ecc_strength = 2;
mtd->owner = THIS_MODULE;
- mtd->erase = doc_erase;
- mtd->read = doc_read;
- mtd->write = doc_write;
- mtd->read_oob = doc_read_oob;
- mtd->write_oob = doc_write_oob;
+ mtd->_erase = doc_erase;
+ mtd->_read = doc_read;
+ mtd->_write = doc_write;
+ mtd->_read_oob = doc_read_oob;
+ mtd->_write_oob = doc_write_oob;
this->curfloor = -1;
this->curchip = -1;
@@ -581,10 +582,6 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
void __iomem * docptr = this->virtadr;
struct Nand *mychip = &this->chips[from >> (this->chipshift)];
- /* Don't allow read past end of device */
- if (from >= this->totlen)
- return -EINVAL;
-
/* Don't allow a single read to cross a 512-byte block boundary */
if (from + len > ((from | 0x1ff) + 1))
len = ((from | 0x1ff) + 1) - from;
@@ -700,10 +697,6 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
void __iomem * docptr = this->virtadr;
struct Nand *mychip = &this->chips[to >> (this->chipshift)];
- /* Don't allow write past end of device */
- if (to >= this->totlen)
- return -EINVAL;
-
/* Don't allow writes which aren't exactly one block (512 bytes) */
if ((to & 0x1ff) || (len != 0x200))
return -EINVAL;
@@ -800,7 +793,6 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to);
/* Error in programming
FIXME: implement Bad Block Replacement (in nftl.c ??) */
- *retlen = 0;
ret = -EIO;
}
dummy = ReadDOC(docptr, Mplus_LastDataRead);
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index ad11ef0a..8272c02 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -80,14 +80,9 @@ static struct nand_ecclayout docg3_oobinfo = {
.oobavail = 8,
};
-/**
- * struct docg3_bch - BCH engine
- */
-static struct bch_control *docg3_bch;
-
static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
{
- u8 val = readb(docg3->base + reg);
+ u8 val = readb(docg3->cascade->base + reg);
trace_docg3_io(0, 8, reg, (int)val);
return val;
@@ -95,7 +90,7 @@ static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
static inline u16 doc_readw(struct docg3 *docg3, u16 reg)
{
- u16 val = readw(docg3->base + reg);
+ u16 val = readw(docg3->cascade->base + reg);
trace_docg3_io(0, 16, reg, (int)val);
return val;
@@ -103,13 +98,13 @@ static inline u16 doc_readw(struct docg3 *docg3, u16 reg)
static inline void doc_writeb(struct docg3 *docg3, u8 val, u16 reg)
{
- writeb(val, docg3->base + reg);
+ writeb(val, docg3->cascade->base + reg);
trace_docg3_io(1, 8, reg, val);
}
static inline void doc_writew(struct docg3 *docg3, u16 val, u16 reg)
{
- writew(val, docg3->base + reg);
+ writew(val, docg3->cascade->base + reg);
trace_docg3_io(1, 16, reg, val);
}
@@ -643,7 +638,8 @@ static int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc)
for (i = 0; i < DOC_ECC_BCH_SIZE; i++)
ecc[i] = bitrev8(hwecc[i]);
- numerrs = decode_bch(docg3_bch, NULL, DOC_ECC_BCH_COVERED_BYTES,
+ numerrs = decode_bch(docg3->cascade->bch, NULL,
+ DOC_ECC_BCH_COVERED_BYTES,
NULL, ecc, NULL, errorpos);
BUG_ON(numerrs == -EINVAL);
if (numerrs < 0)
@@ -734,7 +730,7 @@ err:
* doc_read_page_getbytes - Reads bytes from a prepared page
* @docg3: the device
* @len: the number of bytes to be read (must be a multiple of 4)
- * @buf: the buffer to be filled in
+ * @buf: the buffer to be filled in (or NULL is forget bytes)
* @first: 1 if first time read, DOC_READADDRESS should be set
*
*/
@@ -849,7 +845,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
struct docg3 *docg3 = mtd->priv;
- int block0, block1, page, ret, ofs = 0;
+ int block0, block1, page, ret, skip, ofs = 0;
u8 *oobbuf = ops->oobbuf;
u8 *buf = ops->datbuf;
size_t len, ooblen, nbdata, nboob;
@@ -869,34 +865,36 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
doc_dbg("doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n",
from, ops->mode, buf, len, oobbuf, ooblen);
- if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % DOC_LAYOUT_OOB_SIZE) ||
- (from % DOC_LAYOUT_PAGE_SIZE))
+ if (ooblen % DOC_LAYOUT_OOB_SIZE)
return -EINVAL;
- ret = -EINVAL;
- calc_block_sector(from + len, &block0, &block1, &page, &ofs,
- docg3->reliable);
- if (block1 > docg3->max_block)
- goto err;
+ if (from + len > mtd->size)
+ return -EINVAL;
ops->oobretlen = 0;
ops->retlen = 0;
ret = 0;
+ skip = from % DOC_LAYOUT_PAGE_SIZE;
+ mutex_lock(&docg3->cascade->lock);
while (!ret && (len > 0 || ooblen > 0)) {
- calc_block_sector(from, &block0, &block1, &page, &ofs,
+ calc_block_sector(from - skip, &block0, &block1, &page, &ofs,
docg3->reliable);
- nbdata = min_t(size_t, len, (size_t)DOC_LAYOUT_PAGE_SIZE);
+ nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip);
nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE);
ret = doc_read_page_prepare(docg3, block0, block1, page, ofs);
if (ret < 0)
- goto err;
+ goto out;
ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES);
if (ret < 0)
goto err_in_read;
- ret = doc_read_page_getbytes(docg3, nbdata, buf, 1);
+ ret = doc_read_page_getbytes(docg3, skip, NULL, 1);
+ if (ret < skip)
+ goto err_in_read;
+ ret = doc_read_page_getbytes(docg3, nbdata, buf, 0);
if (ret < nbdata)
goto err_in_read;
- doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE - nbdata,
+ doc_read_page_getbytes(docg3,
+ DOC_LAYOUT_PAGE_SIZE - nbdata - skip,
NULL, 0);
ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0);
if (ret < nboob)
@@ -950,13 +948,15 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
len -= nbdata;
ooblen -= nboob;
from += DOC_LAYOUT_PAGE_SIZE;
+ skip = 0;
}
+out:
+ mutex_unlock(&docg3->cascade->lock);
return ret;
err_in_read:
doc_read_page_finish(docg3);
-err:
- return ret;
+ goto out;
}
/**
@@ -1114,10 +1114,10 @@ static int doc_get_op_status(struct docg3 *docg3)
*/
static int doc_write_erase_wait_status(struct docg3 *docg3)
{
- int status, ret = 0;
+ int i, status, ret = 0;
- if (!doc_is_ready(docg3))
- usleep_range(3000, 3000);
+ for (i = 0; !doc_is_ready(docg3) && i < 5; i++)
+ msleep(20);
if (!doc_is_ready(docg3)) {
doc_dbg("Timeout reached and the chip is still not ready\n");
ret = -EAGAIN;
@@ -1196,18 +1196,19 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
int block0, block1, page, ret, ofs = 0;
doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len);
- doc_set_device_id(docg3, docg3->device_id);
info->state = MTD_ERASE_PENDING;
calc_block_sector(info->addr + info->len, &block0, &block1, &page,
&ofs, docg3->reliable);
ret = -EINVAL;
- if (block1 > docg3->max_block || page || ofs)
+ if (info->addr + info->len > mtd->size || page || ofs)
goto reset_err;
ret = 0;
calc_block_sector(info->addr, &block0, &block1, &page, &ofs,
docg3->reliable);
+ mutex_lock(&docg3->cascade->lock);
+ doc_set_device_id(docg3, docg3->device_id);
doc_set_reliable_mode(docg3);
for (len = info->len; !ret && len > 0; len -= mtd->erasesize) {
info->state = MTD_ERASING;
@@ -1215,6 +1216,7 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info)
block0 += 2;
block1 += 2;
}
+ mutex_unlock(&docg3->cascade->lock);
if (ret)
goto reset_err;
@@ -1401,7 +1403,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
struct mtd_oob_ops *ops)
{
struct docg3 *docg3 = mtd->priv;
- int block0, block1, page, ret, pofs = 0, autoecc, oobdelta;
+ int ret, autoecc, oobdelta;
u8 *oobbuf = ops->oobbuf;
u8 *buf = ops->datbuf;
size_t len, ooblen;
@@ -1438,12 +1440,8 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
if (len && ooblen &&
(len / DOC_LAYOUT_PAGE_SIZE) != (ooblen / oobdelta))
return -EINVAL;
-
- ret = -EINVAL;
- calc_block_sector(ofs + len, &block0, &block1, &page, &pofs,
- docg3->reliable);
- if (block1 > docg3->max_block)
- goto err;
+ if (ofs + len > mtd->size)
+ return -EINVAL;
ops->oobretlen = 0;
ops->retlen = 0;
@@ -1457,6 +1455,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
if (autoecc < 0)
return autoecc;
+ mutex_lock(&docg3->cascade->lock);
while (!ret && len > 0) {
memset(oob, 0, sizeof(oob));
if (ofs == docg3->oob_write_ofs)
@@ -1477,8 +1476,9 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
}
ops->retlen += DOC_LAYOUT_PAGE_SIZE;
}
-err:
+
doc_set_device_id(docg3, 0);
+ mutex_unlock(&docg3->cascade->lock);
return ret;
}
@@ -1535,9 +1535,11 @@ static ssize_t dps0_is_key_locked(struct device *dev,
struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
int dps0;
+ mutex_lock(&docg3->cascade->lock);
doc_set_device_id(docg3, docg3->device_id);
dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS);
doc_set_device_id(docg3, 0);
+ mutex_unlock(&docg3->cascade->lock);
return sprintf(buf, "%d\n", !(dps0 & DOC_DPS_KEY_OK));
}
@@ -1548,9 +1550,11 @@ static ssize_t dps1_is_key_locked(struct device *dev,
struct docg3 *docg3 = sysfs_dev2docg3(dev, attr);
int dps1;
+ mutex_lock(&docg3->cascade->lock);
doc_set_device_id(docg3, docg3->device_id);
dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS);
doc_set_device_id(docg3, 0);
+ mutex_unlock(&docg3->cascade->lock);
return sprintf(buf, "%d\n", !(dps1 & DOC_DPS_KEY_OK));
}
@@ -1565,10 +1569,12 @@ static ssize_t dps0_insert_key(struct device *dev,
if (count != DOC_LAYOUT_DPS_KEY_LENGTH)
return -EINVAL;
+ mutex_lock(&docg3->cascade->lock);
doc_set_device_id(docg3, docg3->device_id);
for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++)
doc_writeb(docg3, buf[i], DOC_DPS0_KEY);
doc_set_device_id(docg3, 0);
+ mutex_unlock(&docg3->cascade->lock);
return count;
}
@@ -1582,10 +1588,12 @@ static ssize_t dps1_insert_key(struct device *dev,
if (count != DOC_LAYOUT_DPS_KEY_LENGTH)
return -EINVAL;
+ mutex_lock(&docg3->cascade->lock);
doc_set_device_id(docg3, docg3->device_id);
for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++)
doc_writeb(docg3, buf[i], DOC_DPS1_KEY);
doc_set_device_id(docg3, 0);
+ mutex_unlock(&docg3->cascade->lock);
return count;
}
@@ -1601,13 +1609,13 @@ static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = {
};
static int doc_register_sysfs(struct platform_device *pdev,
- struct mtd_info **floors)
+ struct docg3_cascade *cascade)
{
int ret = 0, floor, i = 0;
struct device *dev = &pdev->dev;
- for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS && floors[floor];
- floor++)
+ for (floor = 0; !ret && floor < DOC_MAX_NBFLOORS &&
+ cascade->floors[floor]; floor++)
for (i = 0; !ret && i < 4; i++)
ret = device_create_file(dev, &doc_sys_attrs[floor][i]);
if (!ret)
@@ -1621,12 +1629,12 @@ static int doc_register_sysfs(struct platform_device *pdev,
}
static void doc_unregister_sysfs(struct platform_device *pdev,
- struct mtd_info **floors)
+ struct docg3_cascade *cascade)
{
struct device *dev = &pdev->dev;
int floor, i;
- for (floor = 0; floor < DOC_MAX_NBFLOORS && floors[floor];
+ for (floor = 0; floor < DOC_MAX_NBFLOORS && cascade->floors[floor];
floor++)
for (i = 0; i < 4; i++)
device_remove_file(dev, &doc_sys_attrs[floor][i]);
@@ -1640,7 +1648,11 @@ static int dbg_flashctrl_show(struct seq_file *s, void *p)
struct docg3 *docg3 = (struct docg3 *)s->private;
int pos = 0;
- u8 fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
+ u8 fctrl;
+
+ mutex_lock(&docg3->cascade->lock);
+ fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
+ mutex_unlock(&docg3->cascade->lock);
pos += seq_printf(s,
"FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n",
@@ -1658,9 +1670,12 @@ static int dbg_asicmode_show(struct seq_file *s, void *p)
{
struct docg3 *docg3 = (struct docg3 *)s->private;
- int pos = 0;
- int pctrl = doc_register_readb(docg3, DOC_ASICMODE);
- int mode = pctrl & 0x03;
+ int pos = 0, pctrl, mode;
+
+ mutex_lock(&docg3->cascade->lock);
+ pctrl = doc_register_readb(docg3, DOC_ASICMODE);
+ mode = pctrl & 0x03;
+ mutex_unlock(&docg3->cascade->lock);
pos += seq_printf(s,
"%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (",
@@ -1692,7 +1707,11 @@ static int dbg_device_id_show(struct seq_file *s, void *p)
{
struct docg3 *docg3 = (struct docg3 *)s->private;
int pos = 0;
- int id = doc_register_readb(docg3, DOC_DEVICESELECT);
+ int id;
+
+ mutex_lock(&docg3->cascade->lock);
+ id = doc_register_readb(docg3, DOC_DEVICESELECT);
+ mutex_unlock(&docg3->cascade->lock);
pos += seq_printf(s, "DeviceId = %d\n", id);
return pos;
@@ -1705,6 +1724,7 @@ static int dbg_protection_show(struct seq_file *s, void *p)
int pos = 0;
int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high;
+ mutex_lock(&docg3->cascade->lock);
protect = doc_register_readb(docg3, DOC_PROTECTION);
dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS);
dps0_low = doc_register_readw(docg3, DOC_DPS0_ADDRLOW);
@@ -1712,6 +1732,7 @@ static int dbg_protection_show(struct seq_file *s, void *p)
dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS);
dps1_low = doc_register_readw(docg3, DOC_DPS1_ADDRLOW);
dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH);
+ mutex_unlock(&docg3->cascade->lock);
pos += seq_printf(s, "Protection = 0x%02x (",
protect);
@@ -1804,7 +1825,7 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
switch (chip_id) {
case DOC_CHIPID_G3:
- mtd->name = kasprintf(GFP_KERNEL, "DiskOnChip G3 floor %d",
+ mtd->name = kasprintf(GFP_KERNEL, "docg3.%d",
docg3->device_id);
docg3->max_block = 2047;
break;
@@ -1817,16 +1838,17 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES;
if (docg3->reliable == 2)
mtd->erasesize /= 2;
- mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
+ mtd->writebufsize = mtd->writesize = DOC_LAYOUT_PAGE_SIZE;
mtd->oobsize = DOC_LAYOUT_OOB_SIZE;
mtd->owner = THIS_MODULE;
- mtd->erase = doc_erase;
- mtd->read = doc_read;
- mtd->write = doc_write;
- mtd->read_oob = doc_read_oob;
- mtd->write_oob = doc_write_oob;
- mtd->block_isbad = doc_block_isbad;
+ mtd->_erase = doc_erase;
+ mtd->_read = doc_read;
+ mtd->_write = doc_write;
+ mtd->_read_oob = doc_read_oob;
+ mtd->_write_oob = doc_write_oob;
+ mtd->_block_isbad = doc_block_isbad;
mtd->ecclayout = &docg3_oobinfo;
+ mtd->ecc_strength = DOC_ECC_BCH_T;
}
/**
@@ -1834,6 +1856,7 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
* @base: the io space where the device is probed
* @floor: the floor of the probed device
* @dev: the device
+ * @cascade: the cascade of chips this devices will belong to
*
* Checks whether a device at the specified IO range, and floor is available.
*
@@ -1841,8 +1864,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
* if a memory allocation failed. If floor 0 is checked, a reset of the ASIC is
* launched.
*/
-static struct mtd_info *doc_probe_device(void __iomem *base, int floor,
- struct device *dev)
+static struct mtd_info * __init
+doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
{
int ret, bbt_nbpages;
u16 chip_id, chip_id_inv;
@@ -1865,7 +1888,7 @@ static struct mtd_info *doc_probe_device(void __iomem *base, int floor,
docg3->dev = dev;
docg3->device_id = floor;
- docg3->base = base;
+ docg3->cascade = cascade;
doc_set_device_id(docg3, docg3->device_id);
if (!floor)
doc_set_asic_mode(docg3, DOC_ASICMODE_RESET);
@@ -1882,7 +1905,7 @@ static struct mtd_info *doc_probe_device(void __iomem *base, int floor,
switch (chip_id) {
case DOC_CHIPID_G3:
doc_info("Found a G3 DiskOnChip at addr %p, floor %d\n",
- base, floor);
+ docg3->cascade->base, floor);
break;
default:
doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id);
@@ -1927,10 +1950,12 @@ static void doc_release_device(struct mtd_info *mtd)
static int docg3_resume(struct platform_device *pdev)
{
int i;
+ struct docg3_cascade *cascade;
struct mtd_info **docg3_floors, *mtd;
struct docg3 *docg3;
- docg3_floors = platform_get_drvdata(pdev);
+ cascade = platform_get_drvdata(pdev);
+ docg3_floors = cascade->floors;
mtd = docg3_floors[0];
docg3 = mtd->priv;
@@ -1952,11 +1977,13 @@ static int docg3_resume(struct platform_device *pdev)
static int docg3_suspend(struct platform_device *pdev, pm_message_t state)
{
int floor, i;
+ struct docg3_cascade *cascade;
struct mtd_info **docg3_floors, *mtd;
struct docg3 *docg3;
u8 ctrl, pwr_down;
- docg3_floors = platform_get_drvdata(pdev);
+ cascade = platform_get_drvdata(pdev);
+ docg3_floors = cascade->floors;
for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) {
mtd = docg3_floors[floor];
if (!mtd)
@@ -2006,7 +2033,7 @@ static int __init docg3_probe(struct platform_device *pdev)
struct resource *ress;
void __iomem *base;
int ret, floor, found = 0;
- struct mtd_info **docg3_floors;
+ struct docg3_cascade *cascade;
ret = -ENXIO;
ress = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -2017,17 +2044,19 @@ static int __init docg3_probe(struct platform_device *pdev)
base = ioremap(ress->start, DOC_IOSPACE_SIZE);
ret = -ENOMEM;
- docg3_floors = kzalloc(sizeof(*docg3_floors) * DOC_MAX_NBFLOORS,
- GFP_KERNEL);
- if (!docg3_floors)
+ cascade = kzalloc(sizeof(*cascade) * DOC_MAX_NBFLOORS,
+ GFP_KERNEL);
+ if (!cascade)
goto nomem1;
- docg3_bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
+ cascade->base = base;
+ mutex_init(&cascade->lock);
+ cascade->bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
DOC_ECC_BCH_PRIMPOLY);
- if (!docg3_bch)
+ if (!cascade->bch)
goto nomem2;
for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) {
- mtd = doc_probe_device(base, floor, dev);
+ mtd = doc_probe_device(cascade, floor, dev);
if (IS_ERR(mtd)) {
ret = PTR_ERR(mtd);
goto err_probe;
@@ -2038,7 +2067,7 @@ static int __init docg3_probe(struct platform_device *pdev)
else
continue;
}
- docg3_floors[floor] = mtd;
+ cascade->floors[floor] = mtd;
ret = mtd_device_parse_register(mtd, part_probes, NULL, NULL,
0);
if (ret)
@@ -2046,26 +2075,26 @@ static int __init docg3_probe(struct platform_device *pdev)
found++;
}
- ret = doc_register_sysfs(pdev, docg3_floors);
+ ret = doc_register_sysfs(pdev, cascade);
if (ret)
goto err_probe;
if (!found)
goto notfound;
- platform_set_drvdata(pdev, docg3_floors);
- doc_dbg_register(docg3_floors[0]->priv);
+ platform_set_drvdata(pdev, cascade);
+ doc_dbg_register(cascade->floors[0]->priv);
return 0;
notfound:
ret = -ENODEV;
dev_info(dev, "No supported DiskOnChip found\n");
err_probe:
- free_bch(docg3_bch);
+ kfree(cascade->bch);
for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
- if (docg3_floors[floor])
- doc_release_device(docg3_floors[floor]);
+ if (cascade->floors[floor])
+ doc_release_device(cascade->floors[floor]);
nomem2:
- kfree(docg3_floors);
+ kfree(cascade);
nomem1:
iounmap(base);
noress:
@@ -2080,19 +2109,19 @@ noress:
*/
static int __exit docg3_release(struct platform_device *pdev)
{
- struct mtd_info **docg3_floors = platform_get_drvdata(pdev);
- struct docg3 *docg3 = docg3_floors[0]->priv;
- void __iomem *base = docg3->base;
+ struct docg3_cascade *cascade = platform_get_drvdata(pdev);
+ struct docg3 *docg3 = cascade->floors[0]->priv;
+ void __iomem *base = cascade->base;
int floor;
- doc_unregister_sysfs(pdev, docg3_floors);
+ doc_unregister_sysfs(pdev, cascade);
doc_dbg_unregister(docg3);
for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
- if (docg3_floors[floor])
- doc_release_device(docg3_floors[floor]);
+ if (cascade->floors[floor])
+ doc_release_device(cascade->floors[floor]);
- kfree(docg3_floors);
- free_bch(docg3_bch);
+ free_bch(docg3->cascade->bch);
+ kfree(cascade);
iounmap(base);
return 0;
}
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
index db0da43..19fb93f 100644
--- a/drivers/mtd/devices/docg3.h
+++ b/drivers/mtd/devices/docg3.h
@@ -22,6 +22,8 @@
#ifndef _MTD_DOCG3_H
#define _MTD_DOCG3_H
+#include <linux/mtd/mtd.h>
+
/*
* Flash memory areas :
* - 0x0000 .. 0x07ff : IPL
@@ -267,9 +269,23 @@
#define DOC_LAYOUT_DPS_KEY_LENGTH 8
/**
+ * struct docg3_cascade - Cascade of 1 to 4 docg3 chips
+ * @floors: floors (ie. one physical docg3 chip is one floor)
+ * @base: IO space to access all chips in the cascade
+ * @bch: the BCH correcting control structure
+ * @lock: lock to protect docg3 IO space from concurrent accesses
+ */
+struct docg3_cascade {
+ struct mtd_info *floors[DOC_MAX_NBFLOORS];
+ void __iomem *base;
+ struct bch_control *bch;
+ struct mutex lock;
+};
+
+/**
* struct docg3 - DiskOnChip driver private data
* @dev: the device currently under control
- * @base: mapped IO space
+ * @cascade: the cascade this device belongs to
* @device_id: number of the cascaded DoCG3 device (0, 1, 2 or 3)
* @if_cfg: if true, reads are on 16bits, else reads are on 8bits
@@ -287,7 +303,7 @@
*/
struct docg3 {
struct device *dev;
- void __iomem *base;
+ struct docg3_cascade *cascade;
unsigned int device_id:4;
unsigned int if_cfg:1;
unsigned int reliable:2;
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c
index 3a11ea6..82bd00a 100644
--- a/drivers/mtd/devices/lart.c
+++ b/drivers/mtd/devices/lart.c
@@ -367,9 +367,6 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
printk (KERN_DEBUG "%s(addr = 0x%.8x, len = %d)\n", __func__, instr->addr, instr->len);
#endif
- /* sanity checks */
- if (instr->addr + instr->len > mtd->size) return (-EINVAL);
-
/*
* check that both start and end of the requested erase are
* aligned with the erasesize at the appropriate addresses.
@@ -440,10 +437,6 @@ static int flash_read (struct mtd_info *mtd,loff_t from,size_t len,size_t *retle
printk (KERN_DEBUG "%s(from = 0x%.8x, len = %d)\n", __func__, (__u32)from, len);
#endif
- /* sanity checks */
- if (!len) return (0);
- if (from + len > mtd->size) return (-EINVAL);
-
/* we always read len bytes */
*retlen = len;
@@ -522,11 +515,8 @@ static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen
printk (KERN_DEBUG "%s(to = 0x%.8x, len = %d)\n", __func__, (__u32)to, len);
#endif
- *retlen = 0;
-
/* sanity checks */
if (!len) return (0);
- if (to + len > mtd->size) return (-EINVAL);
/* first, we write a 0xFF.... padded byte until we reach a dword boundary */
if (to & (BUSWIDTH - 1))
@@ -630,14 +620,15 @@ static int __init lart_flash_init (void)
mtd.name = module_name;
mtd.type = MTD_NORFLASH;
mtd.writesize = 1;
+ mtd.writebufsize = 4;
mtd.flags = MTD_CAP_NORFLASH;
mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN;
mtd.erasesize = FLASH_BLOCKSIZE_MAIN;
mtd.numeraseregions = ARRAY_SIZE(erase_regions);
mtd.eraseregions = erase_regions;
- mtd.erase = flash_erase;
- mtd.read = flash_read;
- mtd.write = flash_write;
+ mtd._erase = flash_erase;
+ mtd._read = flash_read;
+ mtd._write = flash_write;
mtd.owner = THIS_MODULE;
#ifdef LART_DEBUG
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 7c60ddd..1924d24 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -288,9 +288,6 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
__func__, (long long)instr->addr,
(long long)instr->len);
- /* sanity checks */
- if (instr->addr + instr->len > flash->mtd.size)
- return -EINVAL;
div_u64_rem(instr->len, mtd->erasesize, &rem);
if (rem)
return -EINVAL;
@@ -349,13 +346,6 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
__func__, (u32)from, len);
- /* sanity checks */
- if (!len)
- return 0;
-
- if (from + len > flash->mtd.size)
- return -EINVAL;
-
spi_message_init(&m);
memset(t, 0, (sizeof t));
@@ -371,9 +361,6 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
t[1].len = len;
spi_message_add_tail(&t[1], &m);
- /* Byte count starts at zero. */
- *retlen = 0;
-
mutex_lock(&flash->lock);
/* Wait till previous write/erase is done. */
@@ -417,15 +404,6 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
__func__, (u32)to, len);
- *retlen = 0;
-
- /* sanity checks */
- if (!len)
- return(0);
-
- if (to + len > flash->mtd.size)
- return -EINVAL;
-
spi_message_init(&m);
memset(t, 0, (sizeof t));
@@ -509,15 +487,6 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
__func__, (u32)to, len);
- *retlen = 0;
-
- /* sanity checks */
- if (!len)
- return 0;
-
- if (to + len > flash->mtd.size)
- return -EINVAL;
-
spi_message_init(&m);
memset(t, 0, (sizeof t));
@@ -908,14 +877,14 @@ static int __devinit m25p_probe(struct spi_device *spi)
flash->mtd.writesize = 1;
flash->mtd.flags = MTD_CAP_NORFLASH;
flash->mtd.size = info->sector_size * info->n_sectors;
- flash->mtd.erase = m25p80_erase;
- flash->mtd.read = m25p80_read;
+ flash->mtd._erase = m25p80_erase;
+ flash->mtd._read = m25p80_read;
/* sst flash chips use AAI word program */
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST)
- flash->mtd.write = sst_write;
+ flash->mtd._write = sst_write;
else
- flash->mtd.write = m25p80_write;
+ flash->mtd._write = m25p80_write;
/* prefer "small sector" erase if possible */
if (info->flags & SECT_4K) {
@@ -932,6 +901,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
ppdata.of_node = spi->dev.of_node;
flash->mtd.dev.parent = &spi->dev;
flash->page_size = info->page_size;
+ flash->mtd.writebufsize = flash->page_size;
if (info->addr_width)
flash->addr_width = info->addr_width;
@@ -1004,21 +974,7 @@ static struct spi_driver m25p80_driver = {
*/
};
-
-static int __init m25p80_init(void)
-{
- return spi_register_driver(&m25p80_driver);
-}
-
-
-static void __exit m25p80_exit(void)
-{
- spi_unregister_driver(&m25p80_driver);
-}
-
-
-module_init(m25p80_init);
-module_exit(m25p80_exit);
+module_spi_driver(m25p80_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mike Lavender");
diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c
index 8423fb6..182849d 100644
--- a/drivers/mtd/devices/ms02-nv.c
+++ b/drivers/mtd/devices/ms02-nv.c
@@ -59,12 +59,8 @@ static int ms02nv_read(struct mtd_info *mtd, loff_t from,
{
struct ms02nv_private *mp = mtd->priv;
- if (from + len > mtd->size)
- return -EINVAL;
-
memcpy(buf, mp->uaddr + from, len);
*retlen = len;
-
return 0;
}
@@ -73,12 +69,8 @@ static int ms02nv_write(struct mtd_info *mtd, loff_t to,
{
struct ms02nv_private *mp = mtd->priv;
- if (to + len > mtd->size)
- return -EINVAL;
-
memcpy(mp->uaddr + to, buf, len);
*retlen = len;
-
return 0;
}
@@ -215,8 +207,8 @@ static int __init ms02nv_init_one(ulong addr)
mtd->size = fixsize;
mtd->name = (char *)ms02nv_name;
mtd->owner = THIS_MODULE;
- mtd->read = ms02nv_read;
- mtd->write = ms02nv_write;
+ mtd->_read = ms02nv_read;
+ mtd->_write = ms02nv_write;
mtd->writesize = 1;
ret = -EIO;
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index 236057e..928fb0e 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -164,9 +164,6 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
dev_name(&spi->dev), (long long)instr->addr,
(long long)instr->len);
- /* Sanity checks */
- if (instr->addr + instr->len > mtd->size)
- return -EINVAL;
div_u64_rem(instr->len, priv->page_size, &rem);
if (rem)
return -EINVAL;
@@ -252,14 +249,6 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
pr_debug("%s: read 0x%x..0x%x\n", dev_name(&priv->spi->dev),
(unsigned)from, (unsigned)(from + len));
- *retlen = 0;
-
- /* Sanity checks */
- if (!len)
- return 0;
- if (from + len > mtd->size)
- return -EINVAL;
-
/* Calculate flash page/byte address */
addr = (((unsigned)from / priv->page_size) << priv->page_offset)
+ ((unsigned)from % priv->page_size);
@@ -328,14 +317,6 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
pr_debug("%s: write 0x%x..0x%x\n",
dev_name(&spi->dev), (unsigned)to, (unsigned)(to + len));
- *retlen = 0;
-
- /* Sanity checks */
- if (!len)
- return 0;
- if ((to + len) > mtd->size)
- return -EINVAL;
-
spi_message_init(&msg);
x[0].tx_buf = command = priv->command;
@@ -490,8 +471,6 @@ static ssize_t otp_read(struct spi_device *spi, unsigned base,
if ((off + len) > 64)
len = 64 - off;
- if (len == 0)
- return len;
spi_message_init(&m);
@@ -611,16 +590,16 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,
static char *otp_setup(struct mtd_info *device, char revision)
{
- device->get_fact_prot_info = dataflash_get_otp_info;
- device->read_fact_prot_reg = dataflash_read_fact_otp;
- device->get_user_prot_info = dataflash_get_otp_info;
- device->read_user_prot_reg = dataflash_read_user_otp;
+ device->_get_fact_prot_info = dataflash_get_otp_info;
+ device->_read_fact_prot_reg = dataflash_read_fact_otp;
+ device->_get_user_prot_info = dataflash_get_otp_info;
+ device->_read_user_prot_reg = dataflash_read_user_otp;
/* rev c parts (at45db321c and at45db1281 only!) use a
* different write procedure; not (yet?) implemented.
*/
if (revision > 'c')
- device->write_user_prot_reg = dataflash_write_user_otp;
+ device->_write_user_prot_reg = dataflash_write_user_otp;
return ", OTP";
}
@@ -672,9 +651,9 @@ add_dataflash_otp(struct spi_device *spi, char *name,
device->owner = THIS_MODULE;
device->type = MTD_DATAFLASH;
device->flags = MTD_WRITEABLE;
- device->erase = dataflash_erase;
- device->read = dataflash_read;
- device->write = dataflash_write;
+ device->_erase = dataflash_erase;
+ device->_read = dataflash_read;
+ device->_write = dataflash_write;
device->priv = priv;
device->dev.parent = &spi->dev;
@@ -946,18 +925,7 @@ static struct spi_driver dataflash_driver = {
/* FIXME: investigate suspend and resume... */
};
-static int __init dataflash_init(void)
-{
- return spi_register_driver(&dataflash_driver);
-}
-module_init(dataflash_init);
-
-static void __exit dataflash_exit(void)
-{
- spi_unregister_driver(&dataflash_driver);
-}
-module_exit(dataflash_exit);
-
+module_spi_driver(dataflash_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrew Victor, David Brownell");
diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c
index 2562689..ec59d65 100644
--- a/drivers/mtd/devices/mtdram.c
+++ b/drivers/mtd/devices/mtdram.c
@@ -34,34 +34,23 @@ static struct mtd_info *mtd_info;
static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
- if (instr->addr + instr->len > mtd->size)
- return -EINVAL;
-
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
-
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
-
return 0;
}
static int ram_point(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys)
{
- if (from + len > mtd->size)
- return -EINVAL;
-
- /* can we return a physical address with this driver? */
- if (phys)
- return -EINVAL;
-
*virt = mtd->priv + from;
*retlen = len;
return 0;
}
-static void ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+static int ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
+ return 0;
}
/*
@@ -80,11 +69,7 @@ static unsigned long ram_get_unmapped_area(struct mtd_info *mtd,
static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
- if (from + len > mtd->size)
- return -EINVAL;
-
memcpy(buf, mtd->priv + from, len);
-
*retlen = len;
return 0;
}
@@ -92,11 +77,7 @@ static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
static int ram_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
- if (to + len > mtd->size)
- return -EINVAL;
-
memcpy((char *)mtd->priv + to, buf, len);
-
*retlen = len;
return 0;
}
@@ -126,12 +107,12 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
mtd->priv = mapped_address;
mtd->owner = THIS_MODULE;
- mtd->erase = ram_erase;
- mtd->point = ram_point;
- mtd->unpoint = ram_unpoint;
- mtd->get_unmapped_area = ram_get_unmapped_area;
- mtd->read = ram_read;
- mtd->write = ram_write;
+ mtd->_erase = ram_erase;
+ mtd->_point = ram_point;
+ mtd->_unpoint = ram_unpoint;
+ mtd->_get_unmapped_area = ram_get_unmapped_area;
+ mtd->_read = ram_read;
+ mtd->_write = ram_write;
if (mtd_device_register(mtd, NULL, 0))
return -EIO;
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c
index 23423bd..67823de 100644
--- a/drivers/mtd/devices/phram.c
+++ b/drivers/mtd/devices/phram.c
@@ -33,45 +33,33 @@ struct phram_mtd_list {
static LIST_HEAD(phram_list);
-
static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
u_char *start = mtd->priv;
- if (instr->addr + instr->len > mtd->size)
- return -EINVAL;
-
memset(start + instr->addr, 0xff, instr->len);
- /* This'll catch a few races. Free the thing before returning :)
+ /*
+ * This'll catch a few races. Free the thing before returning :)
* I don't feel at all ashamed. This kind of thing is possible anyway
* with flash, but unlikely.
*/
-
instr->state = MTD_ERASE_DONE;
-
mtd_erase_callback(instr);
-
return 0;
}
static int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys)
{
- if (from + len > mtd->size)
- return -EINVAL;
-
- /* can we return a physical address with this driver? */
- if (phys)
- return -EINVAL;
-
*virt = mtd->priv + from;
*retlen = len;
return 0;
}
-static void phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+static int phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
+ return 0;
}
static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
@@ -79,14 +67,7 @@ static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
{
u_char *start = mtd->priv;
- if (from >= mtd->size)
- return -EINVAL;
-
- if (len > mtd->size - from)
- len = mtd->size - from;
-
memcpy(buf, start + from, len);
-
*retlen = len;
return 0;
}
@@ -96,20 +77,11 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
{
u_char *start = mtd->priv;
- if (to >= mtd->size)
- return -EINVAL;
-
- if (len > mtd->size - to)
- len = mtd->size - to;
-
memcpy(start + to, buf, len);
-
*retlen = len;
return 0;
}
-
-
static void unregister_devices(void)
{
struct phram_mtd_list *this, *safe;
@@ -142,11 +114,11 @@ static int register_device(char *name, unsigned long start, unsigned long len)
new->mtd.name = name;
new->mtd.size = len;
new->mtd.flags = MTD_CAP_RAM;
- new->mtd.erase = phram_erase;
- new->mtd.point = phram_point;
- new->mtd.unpoint = phram_unpoint;
- new->mtd.read = phram_read;
- new->mtd.write = phram_write;
+ new->mtd._erase = phram_erase;
+ new->mtd._point = phram_point;
+ new->mtd._unpoint = phram_unpoint;
+ new->mtd._read = phram_read;
+ new->mtd._write = phram_write;
new->mtd.owner = THIS_MODULE;
new->mtd.type = MTD_RAM;
new->mtd.erasesize = PAGE_SIZE;
@@ -233,7 +205,17 @@ static inline void kill_final_newline(char *str)
return 1; \
} while (0)
-static int phram_setup(const char *val, struct kernel_param *kp)
+/*
+ * This shall contain the module parameter if any. It is of the form:
+ * - phram=<device>,<address>,<size> for module case
+ * - phram.phram=<device>,<address>,<size> for built-in case
+ * We leave 64 bytes for the device name, 12 for the address and 12 for the
+ * size.
+ * Example: phram.phram=rootfs,0xa0000000,512Mi
+ */
+static __initdata char phram_paramline[64+12+12];
+
+static int __init phram_setup(const char *val)
{
char buf[64+12+12], *str = buf;
char *token[3];
@@ -282,12 +264,28 @@ static int phram_setup(const char *val, struct kernel_param *kp)
return ret;
}
-module_param_call(phram, phram_setup, NULL, NULL, 000);
+static int __init phram_param_call(const char *val, struct kernel_param *kp)
+{
+ /*
+ * This function is always called before 'init_phram()', whether
+ * built-in or module.
+ */
+ if (strlen(val) >= sizeof(phram_paramline))
+ return -ENOSPC;
+ strcpy(phram_paramline, val);
+
+ return 0;
+}
+
+module_param_call(phram, phram_param_call, NULL, NULL, 000);
MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\"");
static int __init init_phram(void)
{
+ if (phram_paramline[0])
+ return phram_setup(phram_paramline);
+
return 0;
}
diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c
index ecff765..0c51b98 100644
--- a/drivers/mtd/devices/pmc551.c
+++ b/drivers/mtd/devices/pmc551.c
@@ -93,14 +93,49 @@
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <linux/pci.h>
-
#include <linux/mtd/mtd.h>
-#include <linux/mtd/pmc551.h>
+
+#define PMC551_VERSION \
+ "Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n"
+
+#define PCI_VENDOR_ID_V3_SEMI 0x11b0
+#define PCI_DEVICE_ID_V3_SEMI_V370PDC 0x0200
+
+#define PMC551_PCI_MEM_MAP0 0x50
+#define PMC551_PCI_MEM_MAP1 0x54
+#define PMC551_PCI_MEM_MAP_MAP_ADDR_MASK 0x3ff00000
+#define PMC551_PCI_MEM_MAP_APERTURE_MASK 0x000000f0
+#define PMC551_PCI_MEM_MAP_REG_EN 0x00000002
+#define PMC551_PCI_MEM_MAP_ENABLE 0x00000001
+
+#define PMC551_SDRAM_MA 0x60
+#define PMC551_SDRAM_CMD 0x62
+#define PMC551_DRAM_CFG 0x64
+#define PMC551_SYS_CTRL_REG 0x78
+
+#define PMC551_DRAM_BLK0 0x68
+#define PMC551_DRAM_BLK1 0x6c
+#define PMC551_DRAM_BLK2 0x70
+#define PMC551_DRAM_BLK3 0x74
+#define PMC551_DRAM_BLK_GET_SIZE(x) (524288 << ((x >> 4) & 0x0f))
+#define PMC551_DRAM_BLK_SET_COL_MUX(x, v) (((x) & ~0x00007000) | (((v) & 0x7) << 12))
+#define PMC551_DRAM_BLK_SET_ROW_MUX(x, v) (((x) & ~0x00000f00) | (((v) & 0xf) << 8))
+
+struct mypriv {
+ struct pci_dev *dev;
+ u_char *start;
+ u32 base_map0;
+ u32 curr_map0;
+ u32 asize;
+ struct mtd_info *nextpmc551;
+};
static struct mtd_info *pmc551list;
+static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, void **virt, resource_size_t *phys);
+
static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct mypriv *priv = mtd->priv;
@@ -116,16 +151,6 @@ static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)
#endif
end = instr->addr + instr->len - 1;
-
- /* Is it past the end? */
- if (end > mtd->size) {
-#ifdef CONFIG_MTD_PMC551_DEBUG
- printk(KERN_DEBUG "pmc551_erase() out of bounds (%ld > %ld)\n",
- (long)end, (long)mtd->size);
-#endif
- return -EINVAL;
- }
-
eoff_hi = end & ~(priv->asize - 1);
soff_hi = instr->addr & ~(priv->asize - 1);
eoff_lo = end & (priv->asize - 1);
@@ -179,18 +204,6 @@ static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,
printk(KERN_DEBUG "pmc551_point(%ld, %ld)\n", (long)from, (long)len);
#endif
- if (from + len > mtd->size) {
-#ifdef CONFIG_MTD_PMC551_DEBUG
- printk(KERN_DEBUG "pmc551_point() out of bounds (%ld > %ld)\n",
- (long)from + len, (long)mtd->size);
-#endif
- return -EINVAL;
- }
-
- /* can we return a physical address with this driver? */
- if (phys)
- return -EINVAL;
-
soff_hi = from & ~(priv->asize - 1);
soff_lo = from & (priv->asize - 1);
@@ -206,11 +219,12 @@ static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,
return 0;
}
-static void pmc551_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+static int pmc551_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
#ifdef CONFIG_MTD_PMC551_DEBUG
printk(KERN_DEBUG "pmc551_unpoint()\n");
#endif
+ return 0;
}
static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len,
@@ -229,16 +243,6 @@ static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len,
#endif
end = from + len - 1;
-
- /* Is it past the end? */
- if (end > mtd->size) {
-#ifdef CONFIG_MTD_PMC551_DEBUG
- printk(KERN_DEBUG "pmc551_read() out of bounds (%ld > %ld)\n",
- (long)end, (long)mtd->size);
-#endif
- return -EINVAL;
- }
-
soff_hi = from & ~(priv->asize - 1);
eoff_hi = end & ~(priv->asize - 1);
soff_lo = from & (priv->asize - 1);
@@ -296,16 +300,6 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,
#endif
end = to + len - 1;
- /* Is it past the end? or did the u32 wrap? */
- if (end > mtd->size) {
-#ifdef CONFIG_MTD_PMC551_DEBUG
- printk(KERN_DEBUG "pmc551_write() out of bounds (end: %ld, "
- "size: %ld, to: %ld)\n", (long)end, (long)mtd->size,
- (long)to);
-#endif
- return -EINVAL;
- }
-
soff_hi = to & ~(priv->asize - 1);
eoff_hi = end & ~(priv->asize - 1);
soff_lo = to & (priv->asize - 1);
@@ -359,7 +353,7 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,
* mechanism
* returns the size of the memory region found.
*/
-static u32 fixup_pmc551(struct pci_dev *dev)
+static int fixup_pmc551(struct pci_dev *dev)
{
#ifdef CONFIG_MTD_PMC551_BUGFIX
u32 dram_data;
@@ -669,7 +663,7 @@ static int __init init_pmc551(void)
struct mypriv *priv;
int found = 0;
struct mtd_info *mtd;
- u32 length = 0;
+ int length = 0;
if (msize) {
msize = (1 << (ffs(msize) - 1)) << 20;
@@ -787,11 +781,11 @@ static int __init init_pmc551(void)
mtd->size = msize;
mtd->flags = MTD_CAP_RAM;
- mtd->erase = pmc551_erase;
- mtd->read = pmc551_read;
- mtd->write = pmc551_write;
- mtd->point = pmc551_point;
- mtd->unpoint = pmc551_unpoint;
+ mtd->_erase = pmc551_erase;
+ mtd->_read = pmc551_read;
+ mtd->_write = pmc551_write;
+ mtd->_point = pmc551_point;
+ mtd->_unpoint = pmc551_unpoint;
mtd->type = MTD_RAM;
mtd->name = "PMC551 RAM board";
mtd->erasesize = 0x10000;
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c
index e585263..8f52fc8 100644
--- a/drivers/mtd/devices/slram.c
+++ b/drivers/mtd/devices/slram.c
@@ -42,7 +42,6 @@
#include <linux/ioctl.h>
#include <linux/init.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <linux/mtd/mtd.h>
@@ -76,7 +75,7 @@ static slram_mtd_list_t *slram_mtdlist = NULL;
static int slram_erase(struct mtd_info *, struct erase_info *);
static int slram_point(struct mtd_info *, loff_t, size_t, size_t *, void **,
resource_size_t *);
-static void slram_unpoint(struct mtd_info *, loff_t, size_t);
+static int slram_unpoint(struct mtd_info *, loff_t, size_t);
static int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
@@ -84,21 +83,13 @@ static int slram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
slram_priv_t *priv = mtd->priv;
- if (instr->addr + instr->len > mtd->size) {
- return(-EINVAL);
- }
-
memset(priv->start + instr->addr, 0xff, instr->len);
-
/* This'll catch a few races. Free the thing before returning :)
* I don't feel at all ashamed. This kind of thing is possible anyway
* with flash, but unlikely.
*/
-
instr->state = MTD_ERASE_DONE;
-
mtd_erase_callback(instr);
-
return(0);
}
@@ -107,20 +98,14 @@ static int slram_point(struct mtd_info *mtd, loff_t from, size_t len,
{
slram_priv_t *priv = mtd->priv;
- /* can we return a physical address with this driver? */
- if (phys)
- return -EINVAL;
-
- if (from + len > mtd->size)
- return -EINVAL;
-
*virt = priv->start + from;
*retlen = len;
return(0);
}
-static void slram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+static int slram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
+ return 0;
}
static int slram_read(struct mtd_info *mtd, loff_t from, size_t len,
@@ -128,14 +113,7 @@ static int slram_read(struct mtd_info *mtd, loff_t from, size_t len,
{
slram_priv_t *priv = mtd->priv;
- if (from > mtd->size)
- return -EINVAL;
-
- if (from + len > mtd->size)
- len = mtd->size - from;
-
memcpy(buf, priv->start + from, len);
-
*retlen = len;
return(0);
}
@@ -145,11 +123,7 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len,
{
slram_priv_t *priv = mtd->priv;
- if (to + len > mtd->size)
- return -EINVAL;
-
memcpy(priv->start + to, buf, len);
-
*retlen = len;
return(0);
}
@@ -200,11 +174,11 @@ static int register_device(char *name, unsigned long start, unsigned long length
(*curmtd)->mtdinfo->name = name;
(*curmtd)->mtdinfo->size = length;
(*curmtd)->mtdinfo->flags = MTD_CAP_RAM;
- (*curmtd)->mtdinfo->erase = slram_erase;
- (*curmtd)->mtdinfo->point = slram_point;
- (*curmtd)->mtdinfo->unpoint = slram_unpoint;
- (*curmtd)->mtdinfo->read = slram_read;
- (*curmtd)->mtdinfo->write = slram_write;
+ (*curmtd)->mtdinfo->_erase = slram_erase;
+ (*curmtd)->mtdinfo->_point = slram_point;
+ (*curmtd)->mtdinfo->_unpoint = slram_unpoint;
+ (*curmtd)->mtdinfo->_read = slram_read;
+ (*curmtd)->mtdinfo->_write = slram_write;
(*curmtd)->mtdinfo->owner = THIS_MODULE;
(*curmtd)->mtdinfo->type = MTD_RAM;
(*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ;
diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c
new file mode 100644
index 0000000..797d43c
--- /dev/null
+++ b/drivers/mtd/devices/spear_smi.c
@@ -0,0 +1,1147 @@
+/*
+ * SMI (Serial Memory Controller) device driver for Serial NOR Flash on
+ * SPEAr platform
+ * The serial nor interface is largely based on drivers/mtd/m25p80.c,
+ * however the SPI interface has been replaced by SMI.
+ *
+ * Copyright © 2010 STMicroelectronics.
+ * Ashish Priyadarshi
+ * Shiraz Hashim <shiraz.hashim@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spear_smi.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+/* SMI clock rate */
+#define SMI_MAX_CLOCK_FREQ 50000000 /* 50 MHz */
+
+/* MAX time out to safely come out of a erase or write busy conditions */
+#define SMI_PROBE_TIMEOUT (HZ / 10)
+#define SMI_MAX_TIME_OUT (3 * HZ)
+
+/* timeout for command completion */
+#define SMI_CMD_TIMEOUT (HZ / 10)
+
+/* registers of smi */
+#define SMI_CR1 0x0 /* SMI control register 1 */
+#define SMI_CR2 0x4 /* SMI control register 2 */
+#define SMI_SR 0x8 /* SMI status register */
+#define SMI_TR 0xC /* SMI transmit register */
+#define SMI_RR 0x10 /* SMI receive register */
+
+/* defines for control_reg 1 */
+#define BANK_EN (0xF << 0) /* enables all banks */
+#define DSEL_TIME (0x6 << 4) /* Deselect time 6 + 1 SMI_CK periods */
+#define SW_MODE (0x1 << 28) /* enables SW Mode */
+#define WB_MODE (0x1 << 29) /* Write Burst Mode */
+#define FAST_MODE (0x1 << 15) /* Fast Mode */
+#define HOLD1 (0x1 << 16) /* Clock Hold period selection */
+
+/* defines for control_reg 2 */
+#define SEND (0x1 << 7) /* Send data */
+#define TFIE (0x1 << 8) /* Transmission Flag Interrupt Enable */
+#define WCIE (0x1 << 9) /* Write Complete Interrupt Enable */
+#define RD_STATUS_REG (0x1 << 10) /* reads status reg */
+#define WE (0x1 << 11) /* Write Enable */
+
+#define TX_LEN_SHIFT 0
+#define RX_LEN_SHIFT 4
+#define BANK_SHIFT 12
+
+/* defines for status register */
+#define SR_WIP 0x1 /* Write in progress */
+#define SR_WEL 0x2 /* Write enable latch */
+#define SR_BP0 0x4 /* Block protect 0 */
+#define SR_BP1 0x8 /* Block protect 1 */
+#define SR_BP2 0x10 /* Block protect 2 */
+#define SR_SRWD 0x80 /* SR write protect */
+#define TFF 0x100 /* Transfer Finished Flag */
+#define WCF 0x200 /* Transfer Finished Flag */
+#define ERF1 0x400 /* Forbidden Write Request */
+#define ERF2 0x800 /* Forbidden Access */
+
+#define WM_SHIFT 12
+
+/* flash opcodes */
+#define OPCODE_RDID 0x9f /* Read JEDEC ID */
+
+/* Flash Device Ids maintenance section */
+
+/* data structure to maintain flash ids from different vendors */
+struct flash_device {
+ char *name;
+ u8 erase_cmd;
+ u32 device_id;
+ u32 pagesize;
+ unsigned long sectorsize;
+ unsigned long size_in_bytes;
+};
+
+#define FLASH_ID(n, es, id, psize, ssize, size) \
+{ \
+ .name = n, \
+ .erase_cmd = es, \
+ .device_id = id, \
+ .pagesize = psize, \
+ .sectorsize = ssize, \
+ .size_in_bytes = size \
+}
+
+static struct flash_device flash_devices[] = {
+ FLASH_ID("st m25p16" , 0xd8, 0x00152020, 0x100, 0x10000, 0x200000),
+ FLASH_ID("st m25p32" , 0xd8, 0x00162020, 0x100, 0x10000, 0x400000),
+ FLASH_ID("st m25p64" , 0xd8, 0x00172020, 0x100, 0x10000, 0x800000),
+ FLASH_ID("st m25p128" , 0xd8, 0x00182020, 0x100, 0x40000, 0x1000000),
+ FLASH_ID("st m25p05" , 0xd8, 0x00102020, 0x80 , 0x8000 , 0x10000),
+ FLASH_ID("st m25p10" , 0xd8, 0x00112020, 0x80 , 0x8000 , 0x20000),
+ FLASH_ID("st m25p20" , 0xd8, 0x00122020, 0x100, 0x10000, 0x40000),
+ FLASH_ID("st m25p40" , 0xd8, 0x00132020, 0x100, 0x10000, 0x80000),
+ FLASH_ID("st m25p80" , 0xd8, 0x00142020, 0x100, 0x10000, 0x100000),
+ FLASH_ID("st m45pe10" , 0xd8, 0x00114020, 0x100, 0x10000, 0x20000),
+ FLASH_ID("st m45pe20" , 0xd8, 0x00124020, 0x100, 0x10000, 0x40000),
+ FLASH_ID("st m45pe40" , 0xd8, 0x00134020, 0x100, 0x10000, 0x80000),
+ FLASH_ID("st m45pe80" , 0xd8, 0x00144020, 0x100, 0x10000, 0x100000),
+ FLASH_ID("sp s25fl004" , 0xd8, 0x00120201, 0x100, 0x10000, 0x80000),
+ FLASH_ID("sp s25fl008" , 0xd8, 0x00130201, 0x100, 0x10000, 0x100000),
+ FLASH_ID("sp s25fl016" , 0xd8, 0x00140201, 0x100, 0x10000, 0x200000),
+ FLASH_ID("sp s25fl032" , 0xd8, 0x00150201, 0x100, 0x10000, 0x400000),
+ FLASH_ID("sp s25fl064" , 0xd8, 0x00160201, 0x100, 0x10000, 0x800000),
+ FLASH_ID("atmel 25f512" , 0x52, 0x0065001F, 0x80 , 0x8000 , 0x10000),
+ FLASH_ID("atmel 25f1024" , 0x52, 0x0060001F, 0x100, 0x8000 , 0x20000),
+ FLASH_ID("atmel 25f2048" , 0x52, 0x0063001F, 0x100, 0x10000, 0x40000),
+ FLASH_ID("atmel 25f4096" , 0x52, 0x0064001F, 0x100, 0x10000, 0x80000),
+ FLASH_ID("atmel 25fs040" , 0xd7, 0x0004661F, 0x100, 0x10000, 0x80000),
+ FLASH_ID("mac 25l512" , 0xd8, 0x001020C2, 0x010, 0x10000, 0x10000),
+ FLASH_ID("mac 25l1005" , 0xd8, 0x001120C2, 0x010, 0x10000, 0x20000),
+ FLASH_ID("mac 25l2005" , 0xd8, 0x001220C2, 0x010, 0x10000, 0x40000),
+ FLASH_ID("mac 25l4005" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000),
+ FLASH_ID("mac 25l4005a" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000),
+ FLASH_ID("mac 25l8005" , 0xd8, 0x001420C2, 0x010, 0x10000, 0x100000),
+ FLASH_ID("mac 25l1605" , 0xd8, 0x001520C2, 0x100, 0x10000, 0x200000),
+ FLASH_ID("mac 25l1605a" , 0xd8, 0x001520C2, 0x010, 0x10000, 0x200000),
+ FLASH_ID("mac 25l3205" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000),
+ FLASH_ID("mac 25l3205a" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000),
+ FLASH_ID("mac 25l6405" , 0xd8, 0x001720C2, 0x100, 0x10000, 0x800000),
+};
+
+/* Define spear specific structures */
+
+struct spear_snor_flash;
+
+/**
+ * struct spear_smi - Structure for SMI Device
+ *
+ * @clk: functional clock
+ * @status: current status register of SMI.
+ * @clk_rate: functional clock rate of SMI (default: SMI_MAX_CLOCK_FREQ)
+ * @lock: lock to prevent parallel access of SMI.
+ * @io_base: base address for registers of SMI.
+ * @pdev: platform device
+ * @cmd_complete: queue to wait for command completion of NOR-flash.
+ * @num_flashes: number of flashes actually present on board.
+ * @flash: separate structure for each Serial NOR-flash attached to SMI.
+ */
+struct spear_smi {
+ struct clk *clk;
+ u32 status;
+ unsigned long clk_rate;
+ struct mutex lock;
+ void __iomem *io_base;
+ struct platform_device *pdev;
+ wait_queue_head_t cmd_complete;
+ u32 num_flashes;
+ struct spear_snor_flash *flash[MAX_NUM_FLASH_CHIP];
+};
+
+/**
+ * struct spear_snor_flash - Structure for Serial NOR Flash
+ *
+ * @bank: Bank number(0, 1, 2, 3) for each NOR-flash.
+ * @dev_id: Device ID of NOR-flash.
+ * @lock: lock to manage flash read, write and erase operations
+ * @mtd: MTD info for each NOR-flash.
+ * @num_parts: Total number of partition in each bank of NOR-flash.
+ * @parts: Partition info for each bank of NOR-flash.
+ * @page_size: Page size of NOR-flash.
+ * @base_addr: Base address of NOR-flash.
+ * @erase_cmd: erase command may vary on different flash types
+ * @fast_mode: flash supports read in fast mode
+ */
+struct spear_snor_flash {
+ u32 bank;
+ u32 dev_id;
+ struct mutex lock;
+ struct mtd_info mtd;
+ u32 num_parts;
+ struct mtd_partition *parts;
+ u32 page_size;
+ void __iomem *base_addr;
+ u8 erase_cmd;
+ u8 fast_mode;
+};
+
+static inline struct spear_snor_flash *get_flash_data(struct mtd_info *mtd)
+{
+ return container_of(mtd, struct spear_snor_flash, mtd);
+}
+
+/**
+ * spear_smi_read_sr - Read status register of flash through SMI
+ * @dev: structure of SMI information.
+ * @bank: bank to which flash is connected
+ *
+ * This routine will return the status register of the flash chip present at the
+ * given bank.
+ */
+static int spear_smi_read_sr(struct spear_smi *dev, u32 bank)
+{
+ int ret;
+ u32 ctrlreg1;
+
+ mutex_lock(&dev->lock);
+ dev->status = 0; /* Will be set in interrupt handler */
+
+ ctrlreg1 = readl(dev->io_base + SMI_CR1);
+ /* program smi in hw mode */
+ writel(ctrlreg1 & ~(SW_MODE | WB_MODE), dev->io_base + SMI_CR1);
+
+ /* performing a rsr instruction in hw mode */
+ writel((bank << BANK_SHIFT) | RD_STATUS_REG | TFIE,
+ dev->io_base + SMI_CR2);
+
+ /* wait for tff */
+ ret = wait_event_interruptible_timeout(dev->cmd_complete,
+ dev->status & TFF, SMI_CMD_TIMEOUT);
+
+ /* copy dev->status (lower 16 bits) in order to release lock */
+ if (ret > 0)
+ ret = dev->status & 0xffff;
+ else
+ ret = -EIO;
+
+ /* restore the ctrl regs state */
+ writel(ctrlreg1, dev->io_base + SMI_CR1);
+ writel(0, dev->io_base + SMI_CR2);
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+/**
+ * spear_smi_wait_till_ready - wait till flash is ready
+ * @dev: structure of SMI information.
+ * @bank: flash corresponding to this bank
+ * @timeout: timeout for busy wait condition
+ *
+ * This routine checks for WIP (write in progress) bit in Status register
+ * If successful the routine returns 0 else -EBUSY
+ */
+static int spear_smi_wait_till_ready(struct spear_smi *dev, u32 bank,
+ unsigned long timeout)
+{
+ unsigned long finish;
+ int status;
+
+ finish = jiffies + timeout;
+ do {
+ status = spear_smi_read_sr(dev, bank);
+ if (status < 0)
+ continue; /* try till timeout */
+ else if (!(status & SR_WIP))
+ return 0;
+
+ cond_resched();
+ } while (!time_after_eq(jiffies, finish));
+
+ dev_err(&dev->pdev->dev, "smi controller is busy, timeout\n");
+ return status;
+}
+
+/**
+ * spear_smi_int_handler - SMI Interrupt Handler.
+ * @irq: irq number
+ * @dev_id: structure of SMI device, embedded in dev_id.
+ *
+ * The handler clears all interrupt conditions and records the status in
+ * dev->status which is used by the driver later.
+ */
+static irqreturn_t spear_smi_int_handler(int irq, void *dev_id)
+{
+ u32 status = 0;
+ struct spear_smi *dev = dev_id;
+
+ status = readl(dev->io_base + SMI_SR);
+
+ if (unlikely(!status))
+ return IRQ_NONE;
+
+ /* clear all interrupt conditions */
+ writel(0, dev->io_base + SMI_SR);
+
+ /* copy the status register in dev->status */
+ dev->status |= status;
+
+ /* send the completion */
+ wake_up_interruptible(&dev->cmd_complete);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * spear_smi_hw_init - initializes the smi controller.
+ * @dev: structure of smi device
+ *
+ * this routine initializes the smi controller wit the default values
+ */
+static void spear_smi_hw_init(struct spear_smi *dev)
+{
+ unsigned long rate = 0;
+ u32 prescale = 0;
+ u32 val;
+
+ rate = clk_get_rate(dev->clk);
+
+ /* functional clock of smi */
+ prescale = DIV_ROUND_UP(rate, dev->clk_rate);
+
+ /*
+ * setting the standard values, fast mode, prescaler for
+ * SMI_MAX_CLOCK_FREQ (50MHz) operation and bank enable
+ */
+ val = HOLD1 | BANK_EN | DSEL_TIME | (prescale << 8);
+
+ mutex_lock(&dev->lock);
+ writel(val, dev->io_base + SMI_CR1);
+ mutex_unlock(&dev->lock);
+}
+
+/**
+ * get_flash_index - match chip id from a flash list.
+ * @flash_id: a valid nor flash chip id obtained from board.
+ *
+ * try to validate the chip id by matching from a list, if not found then simply
+ * returns negative. In case of success returns index in to the flash devices
+ * array.
+ */
+static int get_flash_index(u32 flash_id)
+{
+ int index;
+
+ /* Matches chip-id to entire list of 'serial-nor flash' ids */
+ for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
+ if (flash_devices[index].device_id == flash_id)
+ return index;
+ }
+
+ /* Memory chip is not listed and not supported */
+ return -ENODEV;
+}
+
+/**
+ * spear_smi_write_enable - Enable the flash to do write operation
+ * @dev: structure of SMI device
+ * @bank: enable write for flash connected to this bank
+ *
+ * Set write enable latch with Write Enable command.
+ * Returns 0 on success.
+ */
+static int spear_smi_write_enable(struct spear_smi *dev, u32 bank)
+{
+ int ret;
+ u32 ctrlreg1;
+
+ mutex_lock(&dev->lock);
+ dev->status = 0; /* Will be set in interrupt handler */
+
+ ctrlreg1 = readl(dev->io_base + SMI_CR1);
+ /* program smi in h/w mode */
+ writel(ctrlreg1 & ~SW_MODE, dev->io_base + SMI_CR1);
+
+ /* give the flash, write enable command */
+ writel((bank << BANK_SHIFT) | WE | TFIE, dev->io_base + SMI_CR2);
+
+ ret = wait_event_interruptible_timeout(dev->cmd_complete,
+ dev->status & TFF, SMI_CMD_TIMEOUT);
+
+ /* restore the ctrl regs state */
+ writel(ctrlreg1, dev->io_base + SMI_CR1);
+ writel(0, dev->io_base + SMI_CR2);
+
+ if (ret <= 0) {
+ ret = -EIO;
+ dev_err(&dev->pdev->dev,
+ "smi controller failed on write enable\n");
+ } else {
+ /* check whether write mode status is set for required bank */
+ if (dev->status & (1 << (bank + WM_SHIFT)))
+ ret = 0;
+ else {
+ dev_err(&dev->pdev->dev, "couldn't enable write\n");
+ ret = -EIO;
+ }
+ }
+
+ mutex_unlock(&dev->lock);
+ return ret;
+}
+
+static inline u32
+get_sector_erase_cmd(struct spear_snor_flash *flash, u32 offset)
+{
+ u32 cmd;
+ u8 *x = (u8 *)&cmd;
+
+ x[0] = flash->erase_cmd;
+ x[1] = offset >> 16;
+ x[2] = offset >> 8;
+ x[3] = offset;
+
+ return cmd;
+}
+
+/**
+ * spear_smi_erase_sector - erase one sector of flash
+ * @dev: structure of SMI information
+ * @command: erase command to be send
+ * @bank: bank to which this command needs to be send
+ * @bytes: size of command
+ *
+ * Erase one sector of flash memory at offset ``offset'' which is any
+ * address within the sector which should be erased.
+ * Returns 0 if successful, non-zero otherwise.
+ */
+static int spear_smi_erase_sector(struct spear_smi *dev,
+ u32 bank, u32 command, u32 bytes)
+{
+ u32 ctrlreg1 = 0;
+ int ret;
+
+ ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT);
+ if (ret)
+ return ret;
+
+ ret = spear_smi_write_enable(dev, bank);
+ if (ret)
+ return ret;
+
+ mutex_lock(&dev->lock);
+
+ ctrlreg1 = readl(dev->io_base + SMI_CR1);
+ writel((ctrlreg1 | SW_MODE) & ~WB_MODE, dev->io_base + SMI_CR1);
+
+ /* send command in sw mode */
+ writel(command, dev->io_base + SMI_TR);
+
+ writel((bank << BANK_SHIFT) | SEND | TFIE | (bytes << TX_LEN_SHIFT),
+ dev->io_base + SMI_CR2);
+
+ ret = wait_event_interruptible_timeout(dev->cmd_complete,
+ dev->status & TFF, SMI_CMD_TIMEOUT);
+
+ if (ret <= 0) {
+ ret = -EIO;
+ dev_err(&dev->pdev->dev, "sector erase failed\n");
+ } else
+ ret = 0; /* success */
+
+ /* restore ctrl regs */
+ writel(ctrlreg1, dev->io_base + SMI_CR1);
+ writel(0, dev->io_base + SMI_CR2);
+
+ mutex_unlock(&dev->lock);
+ return ret;
+}
+
+/**
+ * spear_mtd_erase - perform flash erase operation as requested by user
+ * @mtd: Provides the memory characteristics
+ * @e_info: Provides the erase information
+ *
+ * Erase an address range on the flash chip. The address range may extend
+ * one or more erase sectors. Return an error is there is a problem erasing.
+ */
+static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info)
+{
+ struct spear_snor_flash *flash = get_flash_data(mtd);
+ struct spear_smi *dev = mtd->priv;
+ u32 addr, command, bank;
+ int len, ret;
+
+ if (!flash || !dev)
+ return -ENODEV;
+
+ bank = flash->bank;
+ if (bank > dev->num_flashes - 1) {
+ dev_err(&dev->pdev->dev, "Invalid Bank Num");
+ return -EINVAL;
+ }
+
+ addr = e_info->addr;
+ len = e_info->len;
+
+ mutex_lock(&flash->lock);
+
+ /* now erase sectors in loop */
+ while (len) {
+ command = get_sector_erase_cmd(flash, addr);
+ /* preparing the command for flash */
+ ret = spear_smi_erase_sector(dev, bank, command, 4);
+ if (ret) {
+ e_info->state = MTD_ERASE_FAILED;
+ mutex_unlock(&flash->lock);
+ return ret;
+ }
+ addr += mtd->erasesize;
+ len -= mtd->erasesize;
+ }
+
+ mutex_unlock(&flash->lock);
+ e_info->state = MTD_ERASE_DONE;
+ mtd_erase_callback(e_info);
+
+ return 0;
+}
+
+/**
+ * spear_mtd_read - performs flash read operation as requested by the user
+ * @mtd: MTD information of the memory bank
+ * @from: Address from which to start read
+ * @len: Number of bytes to be read
+ * @retlen: Fills the Number of bytes actually read
+ * @buf: Fills this after reading
+ *
+ * Read an address range from the flash chip. The address range
+ * may be any size provided it is within the physical boundaries.
+ * Returns 0 on success, non zero otherwise
+ */
+static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u8 *buf)
+{
+ struct spear_snor_flash *flash = get_flash_data(mtd);
+ struct spear_smi *dev = mtd->priv;
+ void *src;
+ u32 ctrlreg1, val;
+ int ret;
+
+ if (!flash || !dev)
+ return -ENODEV;
+
+ if (flash->bank > dev->num_flashes - 1) {
+ dev_err(&dev->pdev->dev, "Invalid Bank Num");
+ return -EINVAL;
+ }
+
+ /* select address as per bank number */
+ src = flash->base_addr + from;
+
+ mutex_lock(&flash->lock);
+
+ /* wait till previous write/erase is done. */
+ ret = spear_smi_wait_till_ready(dev, flash->bank, SMI_MAX_TIME_OUT);
+ if (ret) {
+ mutex_unlock(&flash->lock);
+ return ret;
+ }
+
+ mutex_lock(&dev->lock);
+ /* put smi in hw mode not wbt mode */
+ ctrlreg1 = val = readl(dev->io_base + SMI_CR1);
+ val &= ~(SW_MODE | WB_MODE);
+ if (flash->fast_mode)
+ val |= FAST_MODE;
+
+ writel(val, dev->io_base + SMI_CR1);
+
+ memcpy_fromio(buf, (u8 *)src, len);
+
+ /* restore ctrl reg1 */
+ writel(ctrlreg1, dev->io_base + SMI_CR1);
+ mutex_unlock(&dev->lock);
+
+ *retlen = len;
+ mutex_unlock(&flash->lock);
+
+ return 0;
+}
+
+static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank,
+ void *dest, const void *src, size_t len)
+{
+ int ret;
+ u32 ctrlreg1;
+
+ /* wait until finished previous write command. */
+ ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT);
+ if (ret)
+ return ret;
+
+ /* put smi in write enable */
+ ret = spear_smi_write_enable(dev, bank);
+ if (ret)
+ return ret;
+
+ /* put smi in hw, write burst mode */
+ mutex_lock(&dev->lock);
+
+ ctrlreg1 = readl(dev->io_base + SMI_CR1);
+ writel((ctrlreg1 | WB_MODE) & ~SW_MODE, dev->io_base + SMI_CR1);
+
+ memcpy_toio(dest, src, len);
+
+ writel(ctrlreg1, dev->io_base + SMI_CR1);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+/**
+ * spear_mtd_write - performs write operation as requested by the user.
+ * @mtd: MTD information of the memory bank.
+ * @to: Address to write.
+ * @len: Number of bytes to be written.
+ * @retlen: Number of bytes actually wrote.
+ * @buf: Buffer from which the data to be taken.
+ *
+ * Write an address range to the flash chip. Data must be written in
+ * flash_page_size chunks. The address range may be any size provided
+ * it is within the physical boundaries.
+ * Returns 0 on success, non zero otherwise
+ */
+static int spear_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u8 *buf)
+{
+ struct spear_snor_flash *flash = get_flash_data(mtd);
+ struct spear_smi *dev = mtd->priv;
+ void *dest;
+ u32 page_offset, page_size;
+ int ret;
+
+ if (!flash || !dev)
+ return -ENODEV;
+
+ if (flash->bank > dev->num_flashes - 1) {
+ dev_err(&dev->pdev->dev, "Invalid Bank Num");
+ return -EINVAL;
+ }
+
+ /* select address as per bank number */
+ dest = flash->base_addr + to;
+ mutex_lock(&flash->lock);
+
+ page_offset = (u32)to % flash->page_size;
+
+ /* do if all the bytes fit onto one page */
+ if (page_offset + len <= flash->page_size) {
+ ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf, len);
+ if (!ret)
+ *retlen += len;
+ } else {
+ u32 i;
+
+ /* the size of data remaining on the first page */
+ page_size = flash->page_size - page_offset;
+
+ ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf,
+ page_size);
+ if (ret)
+ goto err_write;
+ else
+ *retlen += page_size;
+
+ /* write everything in pagesize chunks */
+ for (i = page_size; i < len; i += page_size) {
+ page_size = len - i;
+ if (page_size > flash->page_size)
+ page_size = flash->page_size;
+
+ ret = spear_smi_cpy_toio(dev, flash->bank, dest + i,
+ buf + i, page_size);
+ if (ret)
+ break;
+ else
+ *retlen += page_size;
+ }
+ }
+
+err_write:
+ mutex_unlock(&flash->lock);
+
+ return ret;
+}
+
+/**
+ * spear_smi_probe_flash - Detects the NOR Flash chip.
+ * @dev: structure of SMI information.
+ * @bank: bank on which flash must be probed
+ *
+ * This routine will check whether there exists a flash chip on a given memory
+ * bank ID.
+ * Return index of the probed flash in flash devices structure
+ */
+static int spear_smi_probe_flash(struct spear_smi *dev, u32 bank)
+{
+ int ret;
+ u32 val = 0;
+
+ ret = spear_smi_wait_till_ready(dev, bank, SMI_PROBE_TIMEOUT);
+ if (ret)
+ return ret;
+
+ mutex_lock(&dev->lock);
+
+ dev->status = 0; /* Will be set in interrupt handler */
+ /* put smi in sw mode */
+ val = readl(dev->io_base + SMI_CR1);
+ writel(val | SW_MODE, dev->io_base + SMI_CR1);
+
+ /* send readid command in sw mode */
+ writel(OPCODE_RDID, dev->io_base + SMI_TR);
+
+ val = (bank << BANK_SHIFT) | SEND | (1 << TX_LEN_SHIFT) |
+ (3 << RX_LEN_SHIFT) | TFIE;
+ writel(val, dev->io_base + SMI_CR2);
+
+ /* wait for TFF */
+ ret = wait_event_interruptible_timeout(dev->cmd_complete,
+ dev->status & TFF, SMI_CMD_TIMEOUT);
+ if (ret <= 0) {
+ ret = -ENODEV;
+ goto err_probe;
+ }
+
+ /* get memory chip id */
+ val = readl(dev->io_base + SMI_RR);
+ val &= 0x00ffffff;
+ ret = get_flash_index(val);
+
+err_probe:
+ /* clear sw mode */
+ val = readl(dev->io_base + SMI_CR1);
+ writel(val & ~SW_MODE, dev->io_base + SMI_CR1);
+
+ mutex_unlock(&dev->lock);
+ return ret;
+}
+
+
+#ifdef CONFIG_OF
+static int __devinit spear_smi_probe_config_dt(struct platform_device *pdev,
+ struct device_node *np)
+{
+ struct spear_smi_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node *pp = NULL;
+ const __be32 *addr;
+ u32 val;
+ int len;
+ int i = 0;
+
+ if (!np)
+ return -ENODEV;
+
+ of_property_read_u32(np, "clock-rate", &val);
+ pdata->clk_rate = val;
+
+ pdata->board_flash_info = devm_kzalloc(&pdev->dev,
+ sizeof(*pdata->board_flash_info),
+ GFP_KERNEL);
+
+ /* Fill structs for each subnode (flash device) */
+ while ((pp = of_get_next_child(np, pp))) {
+ struct spear_smi_flash_info *flash_info;
+
+ flash_info = &pdata->board_flash_info[i];
+ pdata->np[i] = pp;
+
+ /* Read base-addr and size from DT */
+ addr = of_get_property(pp, "reg", &len);
+ pdata->board_flash_info->mem_base = be32_to_cpup(&addr[0]);
+ pdata->board_flash_info->size = be32_to_cpup(&addr[1]);
+
+ if (of_get_property(pp, "st,smi-fast-mode", NULL))
+ pdata->board_flash_info->fast_mode = 1;
+
+ i++;
+ }
+
+ pdata->num_flashes = i;
+
+ return 0;
+}
+#else
+static int __devinit spear_smi_probe_config_dt(struct platform_device *pdev,
+ struct device_node *np)
+{
+ return -ENOSYS;
+}
+#endif
+
+static int spear_smi_setup_banks(struct platform_device *pdev,
+ u32 bank, struct device_node *np)
+{
+ struct spear_smi *dev = platform_get_drvdata(pdev);
+ struct mtd_part_parser_data ppdata = {};
+ struct spear_smi_flash_info *flash_info;
+ struct spear_smi_plat_data *pdata;
+ struct spear_snor_flash *flash;
+ struct mtd_partition *parts = NULL;
+ int count = 0;
+ int flash_index;
+ int ret = 0;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (bank > pdata->num_flashes - 1)
+ return -EINVAL;
+
+ flash_info = &pdata->board_flash_info[bank];
+ if (!flash_info)
+ return -ENODEV;
+
+ flash = kzalloc(sizeof(*flash), GFP_ATOMIC);
+ if (!flash)
+ return -ENOMEM;
+ flash->bank = bank;
+ flash->fast_mode = flash_info->fast_mode ? 1 : 0;
+ mutex_init(&flash->lock);
+
+ /* verify whether nor flash is really present on board */
+ flash_index = spear_smi_probe_flash(dev, bank);
+ if (flash_index < 0) {
+ dev_info(&dev->pdev->dev, "smi-nor%d not found\n", bank);
+ ret = flash_index;
+ goto err_probe;
+ }
+ /* map the memory for nor flash chip */
+ flash->base_addr = ioremap(flash_info->mem_base, flash_info->size);
+ if (!flash->base_addr) {
+ ret = -EIO;
+ goto err_probe;
+ }
+
+ dev->flash[bank] = flash;
+ flash->mtd.priv = dev;
+
+ if (flash_info->name)
+ flash->mtd.name = flash_info->name;
+ else
+ flash->mtd.name = flash_devices[flash_index].name;
+
+ flash->mtd.type = MTD_NORFLASH;
+ flash->mtd.writesize = 1;
+ flash->mtd.flags = MTD_CAP_NORFLASH;
+ flash->mtd.size = flash_info->size;
+ flash->mtd.erasesize = flash_devices[flash_index].sectorsize;
+ flash->page_size = flash_devices[flash_index].pagesize;
+ flash->mtd.writebufsize = flash->page_size;
+ flash->erase_cmd = flash_devices[flash_index].erase_cmd;
+ flash->mtd._erase = spear_mtd_erase;
+ flash->mtd._read = spear_mtd_read;
+ flash->mtd._write = spear_mtd_write;
+ flash->dev_id = flash_devices[flash_index].device_id;
+
+ dev_info(&dev->pdev->dev, "mtd .name=%s .size=%llx(%lluM)\n",
+ flash->mtd.name, flash->mtd.size,
+ flash->mtd.size / (1024 * 1024));
+
+ dev_info(&dev->pdev->dev, ".erasesize = 0x%x(%uK)\n",
+ flash->mtd.erasesize, flash->mtd.erasesize / 1024);
+
+#ifndef CONFIG_OF
+ if (flash_info->partitions) {
+ parts = flash_info->partitions;
+ count = flash_info->nr_partitions;
+ }
+#endif
+ ppdata.of_node = np;
+
+ ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
+ count);
+ if (ret) {
+ dev_err(&dev->pdev->dev, "Err MTD partition=%d\n", ret);
+ goto err_map;
+ }
+
+ return 0;
+
+err_map:
+ iounmap(flash->base_addr);
+
+err_probe:
+ kfree(flash);
+ return ret;
+}
+
+/**
+ * spear_smi_probe - Entry routine
+ * @pdev: platform device structure
+ *
+ * This is the first routine which gets invoked during booting and does all
+ * initialization/allocation work. The routine looks for available memory banks,
+ * and do proper init for any found one.
+ * Returns 0 on success, non zero otherwise
+ */
+static int __devinit spear_smi_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct spear_smi_plat_data *pdata = NULL;
+ struct spear_smi *dev;
+ struct resource *smi_base;
+ int irq, ret = 0;
+ int i;
+
+ if (np) {
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ pr_err("%s: ERROR: no memory", __func__);
+ ret = -ENOMEM;
+ goto err;
+ }
+ pdev->dev.platform_data = pdata;
+ ret = spear_smi_probe_config_dt(pdev, np);
+ if (ret) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "no platform data\n");
+ goto err;
+ }
+ } else {
+ pdata = dev_get_platdata(&pdev->dev);
+ if (pdata < 0) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "no platform data\n");
+ goto err;
+ }
+ }
+
+ smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!smi_base) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "invalid smi base address\n");
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "invalid smi irq\n");
+ goto err;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_ATOMIC);
+ if (!dev) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "mem alloc fail\n");
+ goto err;
+ }
+
+ smi_base = request_mem_region(smi_base->start, resource_size(smi_base),
+ pdev->name);
+ if (!smi_base) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "request mem region fail\n");
+ goto err_mem;
+ }
+
+ dev->io_base = ioremap(smi_base->start, resource_size(smi_base));
+ if (!dev->io_base) {
+ ret = -EIO;
+ dev_err(&pdev->dev, "ioremap fail\n");
+ goto err_ioremap;
+ }
+
+ dev->pdev = pdev;
+ dev->clk_rate = pdata->clk_rate;
+
+ if (dev->clk_rate < 0 || dev->clk_rate > SMI_MAX_CLOCK_FREQ)
+ dev->clk_rate = SMI_MAX_CLOCK_FREQ;
+
+ dev->num_flashes = pdata->num_flashes;
+
+ if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
+ dev_err(&pdev->dev, "exceeding max number of flashes\n");
+ dev->num_flashes = MAX_NUM_FLASH_CHIP;
+ }
+
+ dev->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk)) {
+ ret = PTR_ERR(dev->clk);
+ goto err_clk;
+ }
+
+ ret = clk_enable(dev->clk);
+ if (ret)
+ goto err_clk_enable;
+
+ ret = request_irq(irq, spear_smi_int_handler, 0, pdev->name, dev);
+ if (ret) {
+ dev_err(&dev->pdev->dev, "SMI IRQ allocation failed\n");
+ goto err_irq;
+ }
+
+ mutex_init(&dev->lock);
+ init_waitqueue_head(&dev->cmd_complete);
+ spear_smi_hw_init(dev);
+ platform_set_drvdata(pdev, dev);
+
+ /* loop for each serial nor-flash which is connected to smi */
+ for (i = 0; i < dev->num_flashes; i++) {
+ ret = spear_smi_setup_banks(pdev, i, pdata->np[i]);
+ if (ret) {
+ dev_err(&dev->pdev->dev, "bank setup failed\n");
+ goto err_bank_setup;
+ }
+ }
+
+ return 0;
+
+err_bank_setup:
+ free_irq(irq, dev);
+ platform_set_drvdata(pdev, NULL);
+err_irq:
+ clk_disable(dev->clk);
+err_clk_enable:
+ clk_put(dev->clk);
+err_clk:
+ iounmap(dev->io_base);
+err_ioremap:
+ release_mem_region(smi_base->start, resource_size(smi_base));
+err_mem:
+ kfree(dev);
+err:
+ return ret;
+}
+
+/**
+ * spear_smi_remove - Exit routine
+ * @pdev: platform device structure
+ *
+ * free all allocations and delete the partitions.
+ */
+static int __devexit spear_smi_remove(struct platform_device *pdev)
+{
+ struct spear_smi *dev;
+ struct spear_smi_plat_data *pdata;
+ struct spear_snor_flash *flash;
+ struct resource *smi_base;
+ int ret;
+ int i, irq;
+
+ dev = platform_get_drvdata(pdev);
+ if (!dev) {
+ dev_err(&pdev->dev, "dev is null\n");
+ return -ENODEV;
+ }
+
+ pdata = dev_get_platdata(&pdev->dev);
+
+ /* clean up for all nor flash */
+ for (i = 0; i < dev->num_flashes; i++) {
+ flash = dev->flash[i];
+ if (!flash)
+ continue;
+
+ /* clean up mtd stuff */
+ ret = mtd_device_unregister(&flash->mtd);
+ if (ret)
+ dev_err(&pdev->dev, "error removing mtd\n");
+
+ iounmap(flash->base_addr);
+ kfree(flash);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ free_irq(irq, dev);
+
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+ iounmap(dev->io_base);
+ kfree(dev);
+
+ smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(smi_base->start, resource_size(smi_base));
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+int spear_smi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct spear_smi *dev = platform_get_drvdata(pdev);
+
+ if (dev && dev->clk)
+ clk_disable(dev->clk);
+
+ return 0;
+}
+
+int spear_smi_resume(struct platform_device *pdev)
+{
+ struct spear_smi *dev = platform_get_drvdata(pdev);
+ int ret = -EPERM;
+
+ if (dev && dev->clk)
+ ret = clk_enable(dev->clk);
+
+ if (!ret)
+ spear_smi_hw_init(dev);
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id spear_smi_id_table[] = {
+ { .compatible = "st,spear600-smi" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, spear_smi_id_table);
+#endif
+
+static struct platform_driver spear_smi_driver = {
+ .driver = {
+ .name = "smi",
+ .bus = &platform_bus_type,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(spear_smi_id_table),
+ },
+ .probe = spear_smi_probe,
+ .remove = __devexit_p(spear_smi_remove),
+ .suspend = spear_smi_suspend,
+ .resume = spear_smi_resume,
+};
+
+static int spear_smi_init(void)
+{
+ return platform_driver_register(&spear_smi_driver);
+}
+module_init(spear_smi_init);
+
+static void spear_smi_exit(void)
+{
+ platform_driver_unregister(&spear_smi_driver);
+}
+module_exit(spear_smi_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ashish Priyadarshi, Shiraz Hashim <shiraz.hashim@st.com>");
+MODULE_DESCRIPTION("MTD SMI driver for serial nor flash chips");
diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c
index 5fc1983..ab8a2f4 100644
--- a/drivers/mtd/devices/sst25l.c
+++ b/drivers/mtd/devices/sst25l.c
@@ -175,9 +175,6 @@ static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
int err;
/* Sanity checks */
- if (instr->addr + instr->len > flash->mtd.size)
- return -EINVAL;
-
if ((uint32_t)instr->len % mtd->erasesize)
return -EINVAL;
@@ -223,16 +220,6 @@ static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len,
unsigned char command[4];
int ret;
- /* Sanity checking */
- if (len == 0)
- return 0;
-
- if (from + len > flash->mtd.size)
- return -EINVAL;
-
- if (retlen)
- *retlen = 0;
-
spi_message_init(&message);
memset(&transfer, 0, sizeof(transfer));
@@ -274,13 +261,6 @@ static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len,
int i, j, ret, bytes, copied = 0;
unsigned char command[5];
- /* Sanity checks */
- if (!len)
- return 0;
-
- if (to + len > flash->mtd.size)
- return -EINVAL;
-
if ((uint32_t)to % mtd->writesize)
return -EINVAL;
@@ -402,10 +382,11 @@ static int __devinit sst25l_probe(struct spi_device *spi)
flash->mtd.flags = MTD_CAP_NORFLASH;
flash->mtd.erasesize = flash_info->erase_size;
flash->mtd.writesize = flash_info->page_size;
+ flash->mtd.writebufsize = flash_info->page_size;
flash->mtd.size = flash_info->page_size * flash_info->nr_pages;
- flash->mtd.erase = sst25l_erase;
- flash->mtd.read = sst25l_read;
- flash->mtd.write = sst25l_write;
+ flash->mtd._erase = sst25l_erase;
+ flash->mtd._read = sst25l_read;
+ flash->mtd._write = sst25l_write;
dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name,
(long long)flash->mtd.size >> 10);
@@ -418,9 +399,9 @@ static int __devinit sst25l_probe(struct spi_device *spi)
flash->mtd.numeraseregions);
- ret = mtd_device_parse_register(&flash->mtd, NULL, 0,
- data ? data->parts : NULL,
- data ? data->nr_parts : 0);
+ ret = mtd_device_parse_register(&flash->mtd, NULL, NULL,
+ data ? data->parts : NULL,
+ data ? data->nr_parts : 0);
if (ret) {
kfree(flash);
dev_set_drvdata(&spi->dev, NULL);
@@ -450,18 +431,7 @@ static struct spi_driver sst25l_driver = {
.remove = __devexit_p(sst25l_remove),
};
-static int __init sst25l_init(void)
-{
- return spi_register_driver(&sst25l_driver);
-}
-
-static void __exit sst25l_exit(void)
-{
- spi_unregister_driver(&sst25l_driver);
-}
-
-module_init(sst25l_init);
-module_exit(sst25l_exit);
+module_spi_driver(sst25l_driver);
MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips");
MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, "
diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c
index 28646c9..3af3514 100644
--- a/drivers/mtd/inftlcore.c
+++ b/drivers/mtd/inftlcore.c
@@ -56,7 +56,7 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
if (memcmp(mtd->name, "DiskOnChip", 10))
return;
- if (!mtd->block_isbad) {
+ if (!mtd->_block_isbad) {
printk(KERN_ERR
"INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
"Please use the new diskonchip driver under the NAND subsystem.\n");
diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c
index 536bbce..d3cfe26b 100644
--- a/drivers/mtd/lpddr/lpddr_cmds.c
+++ b/drivers/mtd/lpddr/lpddr_cmds.c
@@ -40,7 +40,7 @@ static int lpddr_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static int lpddr_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
size_t *retlen, void **mtdbuf, resource_size_t *phys);
-static void lpddr_unpoint(struct mtd_info *mtd, loff_t adr, size_t len);
+static int lpddr_unpoint(struct mtd_info *mtd, loff_t adr, size_t len);
static int get_chip(struct map_info *map, struct flchip *chip, int mode);
static int chip_ready(struct map_info *map, struct flchip *chip, int mode);
static void put_chip(struct map_info *map, struct flchip *chip);
@@ -63,18 +63,18 @@ struct mtd_info *lpddr_cmdset(struct map_info *map)
mtd->type = MTD_NORFLASH;
/* Fill in the default mtd operations */
- mtd->read = lpddr_read;
+ mtd->_read = lpddr_read;
mtd->type = MTD_NORFLASH;
mtd->flags = MTD_CAP_NORFLASH;
mtd->flags &= ~MTD_BIT_WRITEABLE;
- mtd->erase = lpddr_erase;
- mtd->write = lpddr_write_buffers;
- mtd->writev = lpddr_writev;
- mtd->lock = lpddr_lock;
- mtd->unlock = lpddr_unlock;
+ mtd->_erase = lpddr_erase;
+ mtd->_write = lpddr_write_buffers;
+ mtd->_writev = lpddr_writev;
+ mtd->_lock = lpddr_lock;
+ mtd->_unlock = lpddr_unlock;
if (map_is_linear(map)) {
- mtd->point = lpddr_point;
- mtd->unpoint = lpddr_unpoint;
+ mtd->_point = lpddr_point;
+ mtd->_unpoint = lpddr_unpoint;
}
mtd->size = 1 << lpddr->qinfo->DevSizeShift;
mtd->erasesize = 1 << lpddr->qinfo->UniformBlockSizeShift;
@@ -530,14 +530,12 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
struct flchip *chip = &lpddr->chips[chipnum];
int ret = 0;
- if (!map->virt || (adr + len > mtd->size))
+ if (!map->virt)
return -EINVAL;
/* ofs: offset within the first chip that the first read should start */
ofs = adr - (chipnum << lpddr->chipshift);
-
*mtdbuf = (void *)map->virt + chip->start + ofs;
- *retlen = 0;
while (len) {
unsigned long thislen;
@@ -575,11 +573,11 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
return 0;
}
-static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
+static int lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
{
struct map_info *map = mtd->priv;
struct lpddr_private *lpddr = map->fldrv_priv;
- int chipnum = adr >> lpddr->chipshift;
+ int chipnum = adr >> lpddr->chipshift, err = 0;
unsigned long ofs;
/* ofs: offset within the first chip that the first read should start */
@@ -603,9 +601,11 @@ static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
chip->ref_point_counter--;
if (chip->ref_point_counter == 0)
chip->state = FL_READY;
- } else
+ } else {
printk(KERN_WARNING "%s: Warning: unpoint called on non"
"pointed region\n", map->name);
+ err = -EINVAL;
+ }
put_chip(map, chip);
mutex_unlock(&chip->mutex);
@@ -614,6 +614,8 @@ static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
ofs = 0;
chipnum++;
}
+
+ return err;
}
static int lpddr_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
@@ -637,13 +639,11 @@ static int lpddr_writev(struct mtd_info *mtd, const struct kvec *vecs,
int chipnum;
unsigned long ofs, vec_seek, i;
int wbufsize = 1 << lpddr->qinfo->BufSizeShift;
-
size_t len = 0;
for (i = 0; i < count; i++)
len += vecs[i].iov_len;
- *retlen = 0;
if (!len)
return 0;
@@ -688,9 +688,6 @@ static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr)
ofs = instr->addr;
len = instr->len;
- if (ofs > mtd->size || (len + ofs) > mtd->size)
- return -EINVAL;
-
while (len > 0) {
ret = do_erase_oneblock(mtd, ofs);
if (ret)
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 6c5c431..8af67cf 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -1,5 +1,6 @@
menu "Mapping drivers for chip access"
depends on MTD!=n
+ depends on HAS_IOMEM
config MTD_COMPLEX_MAPPINGS
bool "Support non-linear mappings of flash chips"
diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c
index 650126c..ef5cde8 100644
--- a/drivers/mtd/maps/bfin-async-flash.c
+++ b/drivers/mtd/maps/bfin-async-flash.c
@@ -164,8 +164,8 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev)
return -ENXIO;
}
- mtd_device_parse_register(state->mtd, part_probe_types, 0,
- pdata->parts, pdata->nr_parts);
+ mtd_device_parse_register(state->mtd, part_probe_types, NULL,
+ pdata->parts, pdata->nr_parts);
platform_set_drvdata(pdev, state);
diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c
index f43b365..080f060 100644
--- a/drivers/mtd/maps/dc21285.c
+++ b/drivers/mtd/maps/dc21285.c
@@ -196,7 +196,7 @@ static int __init init_dc21285(void)
dc21285_mtd->owner = THIS_MODULE;
- mtd_device_parse_register(dc21285_mtd, probes, 0, NULL, 0);
+ mtd_device_parse_register(dc21285_mtd, probes, NULL, NULL, 0);
if(machine_is_ebsa285()) {
/*
diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
index 33cce89..e4de96b 100644
--- a/drivers/mtd/maps/gpio-addr-flash.c
+++ b/drivers/mtd/maps/gpio-addr-flash.c
@@ -252,8 +252,8 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev)
}
- mtd_device_parse_register(state->mtd, part_probe_types, 0,
- pdata->parts, pdata->nr_parts);
+ mtd_device_parse_register(state->mtd, part_probe_types, NULL,
+ pdata->parts, pdata->nr_parts);
return 0;
}
diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c
index 49c1418..8ed6cb4 100644
--- a/drivers/mtd/maps/h720x-flash.c
+++ b/drivers/mtd/maps/h720x-flash.c
@@ -85,8 +85,8 @@ static int __init h720x_mtd_init(void)
if (mymtd) {
mymtd->owner = THIS_MODULE;
- mtd_device_parse_register(mymtd, NULL, 0,
- h720x_partitions, NUM_PARTITIONS);
+ mtd_device_parse_register(mymtd, NULL, NULL,
+ h720x_partitions, NUM_PARTITIONS);
return 0;
}
diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c
index f47aedb..834a06c 100644
--- a/drivers/mtd/maps/impa7.c
+++ b/drivers/mtd/maps/impa7.c
@@ -91,7 +91,7 @@ static int __init init_impa7(void)
if (impa7_mtd[i]) {
impa7_mtd[i]->owner = THIS_MODULE;
devicesfound++;
- mtd_device_parse_register(impa7_mtd[i], NULL, 0,
+ mtd_device_parse_register(impa7_mtd[i], NULL, NULL,
partitions,
ARRAY_SIZE(partitions));
}
diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c
index 08c2396..92e1f41 100644
--- a/drivers/mtd/maps/intel_vr_nor.c
+++ b/drivers/mtd/maps/intel_vr_nor.c
@@ -72,7 +72,7 @@ static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p)
{
/* register the flash bank */
/* partition the flash bank */
- return mtd_device_parse_register(p->info, NULL, 0, NULL, 0);
+ return mtd_device_parse_register(p->info, NULL, NULL, NULL, 0);
}
static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c
index fc7d4d0..4a41ced 100644
--- a/drivers/mtd/maps/ixp2000.c
+++ b/drivers/mtd/maps/ixp2000.c
@@ -226,7 +226,7 @@ static int ixp2000_flash_probe(struct platform_device *dev)
}
info->mtd->owner = THIS_MODULE;
- err = mtd_device_parse_register(info->mtd, probes, 0, NULL, 0);
+ err = mtd_device_parse_register(info->mtd, probes, NULL, NULL, 0);
if (err)
goto Error;
diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c
index 8b54101..e864fc6 100644
--- a/drivers/mtd/maps/ixp4xx.c
+++ b/drivers/mtd/maps/ixp4xx.c
@@ -182,6 +182,9 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
{
struct flash_platform_data *plat = dev->dev.platform_data;
struct ixp4xx_flash_info *info;
+ struct mtd_part_parser_data ppdata = {
+ .origin = dev->resource->start,
+ };
int err = -1;
if (!plat)
@@ -247,7 +250,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
/* Use the fast version */
info->map.write = ixp4xx_write16;
- err = mtd_device_parse_register(info->mtd, probes, dev->resource->start,
+ err = mtd_device_parse_register(info->mtd, probes, &ppdata,
plat->parts, plat->nr_parts);
if (err) {
printk(KERN_ERR "Could not parse partitions\n");
diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c
index dd0360b..74bd98e 100644
--- a/drivers/mtd/maps/l440gx.c
+++ b/drivers/mtd/maps/l440gx.c
@@ -27,17 +27,21 @@ static struct mtd_info *mymtd;
/* Is this really the vpp port? */
+static DEFINE_SPINLOCK(l440gx_vpp_lock);
+static int l440gx_vpp_refcnt;
static void l440gx_set_vpp(struct map_info *map, int vpp)
{
- unsigned long l;
+ unsigned long flags;
- l = inl(VPP_PORT);
+ spin_lock_irqsave(&l440gx_vpp_lock, flags);
if (vpp) {
- l |= 1;
+ if (++l440gx_vpp_refcnt == 1) /* first nested 'on' */
+ outl(inl(VPP_PORT) | 1, VPP_PORT);
} else {
- l &= ~1;
+ if (--l440gx_vpp_refcnt == 0) /* last nested 'off' */
+ outl(inl(VPP_PORT) & ~1, VPP_PORT);
}
- outl(l, VPP_PORT);
+ spin_unlock_irqrestore(&l440gx_vpp_lock, flags);
}
static struct map_info l440gx_map = {
diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c
index 7b889de..b5401e3 100644
--- a/drivers/mtd/maps/lantiq-flash.c
+++ b/drivers/mtd/maps/lantiq-flash.c
@@ -45,6 +45,7 @@ struct ltq_mtd {
};
static char ltq_map_name[] = "ltq_nor";
+static const char *ltq_probe_types[] __devinitconst = { "cmdlinepart", NULL };
static map_word
ltq_read16(struct map_info *map, unsigned long adr)
@@ -168,8 +169,9 @@ ltq_mtd_probe(struct platform_device *pdev)
cfi->addr_unlock1 ^= 1;
cfi->addr_unlock2 ^= 1;
- err = mtd_device_parse_register(ltq_mtd->mtd, NULL, 0,
- ltq_mtd_data->parts, ltq_mtd_data->nr_parts);
+ err = mtd_device_parse_register(ltq_mtd->mtd, ltq_probe_types, NULL,
+ ltq_mtd_data->parts,
+ ltq_mtd_data->nr_parts);
if (err) {
dev_err(&pdev->dev, "failed to add partitions\n");
goto err_destroy;
diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c
index 8fed58e..3c7ad17 100644
--- a/drivers/mtd/maps/latch-addr-flash.c
+++ b/drivers/mtd/maps/latch-addr-flash.c
@@ -199,8 +199,9 @@ static int __devinit latch_addr_flash_probe(struct platform_device *dev)
}
info->mtd->owner = THIS_MODULE;
- mtd_device_parse_register(info->mtd, NULL, 0,
- latch_addr_data->parts, latch_addr_data->nr_parts);
+ mtd_device_parse_register(info->mtd, NULL, NULL,
+ latch_addr_data->parts,
+ latch_addr_data->nr_parts);
return 0;
iounmap:
diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c
index e8e9fec..a3cfad3 100644
--- a/drivers/mtd/maps/pcmciamtd.c
+++ b/drivers/mtd/maps/pcmciamtd.c
@@ -14,7 +14,6 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
@@ -295,13 +294,24 @@ static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *f
}
+static DEFINE_SPINLOCK(pcmcia_vpp_lock);
+static int pcmcia_vpp_refcnt;
static void pcmciamtd_set_vpp(struct map_info *map, int on)
{
struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
struct pcmcia_device *link = dev->p_dev;
+ unsigned long flags;
pr_debug("dev = %p on = %d vpp = %d\n\n", dev, on, dev->vpp);
- pcmcia_fixup_vpp(link, on ? dev->vpp : 0);
+ spin_lock_irqsave(&pcmcia_vpp_lock, flags);
+ if (on) {
+ if (++pcmcia_vpp_refcnt == 1) /* first nested 'on' */
+ pcmcia_fixup_vpp(link, dev->vpp);
+ } else {
+ if (--pcmcia_vpp_refcnt == 0) /* last nested 'off' */
+ pcmcia_fixup_vpp(link, 0);
+ }
+ spin_unlock_irqrestore(&pcmcia_vpp_lock, flags);
}
diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c
index abc5626..21b0b71 100644
--- a/drivers/mtd/maps/physmap.c
+++ b/drivers/mtd/maps/physmap.c
@@ -27,6 +27,8 @@ struct physmap_flash_info {
struct mtd_info *mtd[MAX_RESOURCES];
struct mtd_info *cmtd;
struct map_info map[MAX_RESOURCES];
+ spinlock_t vpp_lock;
+ int vpp_refcnt;
};
static int physmap_flash_remove(struct platform_device *dev)
@@ -63,12 +65,26 @@ static void physmap_set_vpp(struct map_info *map, int state)
{
struct platform_device *pdev;
struct physmap_flash_data *physmap_data;
+ struct physmap_flash_info *info;
+ unsigned long flags;
pdev = (struct platform_device *)map->map_priv_1;
physmap_data = pdev->dev.platform_data;
- if (physmap_data->set_vpp)
- physmap_data->set_vpp(pdev, state);
+ if (!physmap_data->set_vpp)
+ return;
+
+ info = platform_get_drvdata(pdev);
+
+ spin_lock_irqsave(&info->vpp_lock, flags);
+ if (state) {
+ if (++info->vpp_refcnt == 1) /* first nested 'on' */
+ physmap_data->set_vpp(pdev, 1);
+ } else {
+ if (--info->vpp_refcnt == 0) /* last nested 'off' */
+ physmap_data->set_vpp(pdev, 0);
+ }
+ spin_unlock_irqrestore(&info->vpp_lock, flags);
}
static const char *rom_probe_types[] = {
@@ -172,9 +188,11 @@ static int physmap_flash_probe(struct platform_device *dev)
if (err)
goto err_out;
+ spin_lock_init(&info->vpp_lock);
+
part_types = physmap_data->part_probe_types ? : part_probe_types;
- mtd_device_parse_register(info->cmtd, part_types, 0,
+ mtd_device_parse_register(info->cmtd, part_types, NULL,
physmap_data->parts, physmap_data->nr_parts);
return 0;
diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c
index 45876d0..891558d 100644
--- a/drivers/mtd/maps/plat-ram.c
+++ b/drivers/mtd/maps/plat-ram.c
@@ -222,8 +222,9 @@ static int platram_probe(struct platform_device *pdev)
/* check to see if there are any available partitions, or wether
* to add this device whole */
- err = mtd_device_parse_register(info->mtd, pdata->probes, 0,
- pdata->partitions, pdata->nr_partitions);
+ err = mtd_device_parse_register(info->mtd, pdata->probes, NULL,
+ pdata->partitions,
+ pdata->nr_partitions);
if (!err)
dev_info(&pdev->dev, "registered mtd device\n");
diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c
index 436d121..81884c2 100644
--- a/drivers/mtd/maps/pxa2xx-flash.c
+++ b/drivers/mtd/maps/pxa2xx-flash.c
@@ -98,7 +98,8 @@ static int __devinit pxa2xx_flash_probe(struct platform_device *pdev)
}
info->mtd->owner = THIS_MODULE;
- mtd_device_parse_register(info->mtd, probes, 0, flash->parts, flash->nr_parts);
+ mtd_device_parse_register(info->mtd, probes, NULL, flash->parts,
+ flash->nr_parts);
platform_set_drvdata(pdev, info);
return 0;
diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c
index 3da63fc..6f52e1f 100644
--- a/drivers/mtd/maps/rbtx4939-flash.c
+++ b/drivers/mtd/maps/rbtx4939-flash.c
@@ -102,8 +102,8 @@ static int rbtx4939_flash_probe(struct platform_device *dev)
info->mtd->owner = THIS_MODULE;
if (err)
goto err_out;
- err = mtd_device_parse_register(info->mtd, NULL, 0,
- pdata->parts, pdata->nr_parts);
+ err = mtd_device_parse_register(info->mtd, NULL, NULL, pdata->parts,
+ pdata->nr_parts);
if (err)
goto err_out;
diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c
index 5028219..a675bdb 100644
--- a/drivers/mtd/maps/sa1100-flash.c
+++ b/drivers/mtd/maps/sa1100-flash.c
@@ -23,106 +23,6 @@
#include <asm/sizes.h>
#include <asm/mach/flash.h>
-#if 0
-/*
- * This is here for documentation purposes only - until these people
- * submit their machine types. It will be gone January 2005.
- */
-static struct mtd_partition consus_partitions[] = {
- {
- .name = "Consus boot firmware",
- .offset = 0,
- .size = 0x00040000,
- .mask_flags = MTD_WRITABLE, /* force read-only */
- }, {
- .name = "Consus kernel",
- .offset = 0x00040000,
- .size = 0x00100000,
- .mask_flags = 0,
- }, {
- .name = "Consus disk",
- .offset = 0x00140000,
- /* The rest (up to 16M) for jffs. We could put 0 and
- make it find the size automatically, but right now
- i have 32 megs. jffs will use all 32 megs if given
- the chance, and this leads to horrible problems
- when you try to re-flash the image because blob
- won't erase the whole partition. */
- .size = 0x01000000 - 0x00140000,
- .mask_flags = 0,
- }, {
- /* this disk is a secondary disk, which can be used as
- needed, for simplicity, make it the size of the other
- consus partition, although realistically it could be
- the remainder of the disk (depending on the file
- system used) */
- .name = "Consus disk2",
- .offset = 0x01000000,
- .size = 0x01000000 - 0x00140000,
- .mask_flags = 0,
- }
-};
-
-/* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */
-static struct mtd_partition frodo_partitions[] =
-{
- {
- .name = "bootloader",
- .size = 0x00040000,
- .offset = 0x00000000,
- .mask_flags = MTD_WRITEABLE
- }, {
- .name = "bootloader params",
- .size = 0x00040000,
- .offset = MTDPART_OFS_APPEND,
- .mask_flags = MTD_WRITEABLE
- }, {
- .name = "kernel",
- .size = 0x00100000,
- .offset = MTDPART_OFS_APPEND,
- .mask_flags = MTD_WRITEABLE
- }, {
- .name = "ramdisk",
- .size = 0x00400000,
- .offset = MTDPART_OFS_APPEND,
- .mask_flags = MTD_WRITEABLE
- }, {
- .name = "file system",
- .size = MTDPART_SIZ_FULL,
- .offset = MTDPART_OFS_APPEND
- }
-};
-
-static struct mtd_partition jornada56x_partitions[] = {
- {
- .name = "bootldr",
- .size = 0x00040000,
- .offset = 0,
- .mask_flags = MTD_WRITEABLE,
- }, {
- .name = "rootfs",
- .size = MTDPART_SIZ_FULL,
- .offset = MTDPART_OFS_APPEND,
- }
-};
-
-static void jornada56x_set_vpp(int vpp)
-{
- if (vpp)
- GPSR = GPIO_GPIO26;
- else
- GPCR = GPIO_GPIO26;
- GPDR |= GPIO_GPIO26;
-}
-
-/*
- * Machine Phys Size set_vpp
- * Consus : SA1100_CS0_PHYS SZ_32M
- * Frodo : SA1100_CS0_PHYS SZ_32M
- * Jornada56x: SA1100_CS0_PHYS SZ_32M jornada56x_set_vpp
- */
-#endif
-
struct sa_subdev_info {
char name[16];
struct map_info map;
@@ -136,10 +36,22 @@ struct sa_info {
struct sa_subdev_info subdev[0];
};
+static DEFINE_SPINLOCK(sa1100_vpp_lock);
+static int sa1100_vpp_refcnt;
static void sa1100_set_vpp(struct map_info *map, int on)
{
struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
- subdev->plat->set_vpp(on);
+ unsigned long flags;
+
+ spin_lock_irqsave(&sa1100_vpp_lock, flags);
+ if (on) {
+ if (++sa1100_vpp_refcnt == 1) /* first nested 'on' */
+ subdev->plat->set_vpp(1);
+ } else {
+ if (--sa1100_vpp_refcnt == 0) /* last nested 'off' */
+ subdev->plat->set_vpp(0);
+ }
+ spin_unlock_irqrestore(&sa1100_vpp_lock, flags);
}
static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
@@ -352,8 +264,8 @@ static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
/*
* Partition selection stuff.
*/
- mtd_device_parse_register(info->mtd, part_probes, 0,
- plat->parts, plat->nr_parts);
+ mtd_device_parse_register(info->mtd, part_probes, NULL, plat->parts,
+ plat->nr_parts);
platform_set_drvdata(pdev, info);
err = 0;
@@ -373,21 +285,9 @@ static int __exit sa1100_mtd_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
-static void sa1100_mtd_shutdown(struct platform_device *dev)
-{
- struct sa_info *info = platform_get_drvdata(dev);
- if (info && mtd_suspend(info->mtd) == 0)
- mtd_resume(info->mtd);
-}
-#else
-#define sa1100_mtd_shutdown NULL
-#endif
-
static struct platform_driver sa1100_mtd_driver = {
.probe = sa1100_mtd_probe,
.remove = __exit_p(sa1100_mtd_remove),
- .shutdown = sa1100_mtd_shutdown,
.driver = {
.name = "sa1100-mtd",
.owner = THIS_MODULE,
diff --git a/drivers/mtd/maps/solutionengine.c b/drivers/mtd/maps/solutionengine.c
index 496c407..9d900ad 100644
--- a/drivers/mtd/maps/solutionengine.c
+++ b/drivers/mtd/maps/solutionengine.c
@@ -92,8 +92,8 @@ static int __init init_soleng_maps(void)
mtd_device_register(eprom_mtd, NULL, 0);
}
- mtd_device_parse_register(flash_mtd, probes, 0,
- superh_se_partitions, NUM_PARTITIONS);
+ mtd_device_parse_register(flash_mtd, probes, NULL,
+ superh_se_partitions, NUM_PARTITIONS);
return 0;
}
diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c
index 6793074..cfff454 100644
--- a/drivers/mtd/maps/uclinux.c
+++ b/drivers/mtd/maps/uclinux.c
@@ -85,7 +85,7 @@ static int __init uclinux_mtd_init(void)
}
mtd->owner = THIS_MODULE;
- mtd->point = uclinux_point;
+ mtd->_point = uclinux_point;
mtd->priv = mapp;
uclinux_ram_mtdinfo = mtd;
diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c
index 3a04b07..2e2b094 100644
--- a/drivers/mtd/maps/vmu-flash.c
+++ b/drivers/mtd/maps/vmu-flash.c
@@ -360,9 +360,6 @@ static int vmu_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
int index = 0, retval, partition, leftover, numblocks;
unsigned char cx;
- if (len < 1)
- return -EIO;
-
mpart = mtd->priv;
mdev = mpart->mdev;
partition = mpart->partition;
@@ -434,11 +431,6 @@ static int vmu_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
partition = mpart->partition;
card = maple_get_drvdata(mdev);
- /* simple sanity checks */
- if (len < 1) {
- error = -EIO;
- goto failed;
- }
numblocks = card->parts[partition].numblocks;
if (to + len > numblocks * card->blocklen)
len = numblocks * card->blocklen - to;
@@ -544,9 +536,9 @@ static void vmu_queryblocks(struct mapleq *mq)
mtd_cur->flags = MTD_WRITEABLE|MTD_NO_ERASE;
mtd_cur->size = part_cur->numblocks * card->blocklen;
mtd_cur->erasesize = card->blocklen;
- mtd_cur->write = vmu_flash_write;
- mtd_cur->read = vmu_flash_read;
- mtd_cur->sync = vmu_flash_sync;
+ mtd_cur->_write = vmu_flash_write;
+ mtd_cur->_read = vmu_flash_read;
+ mtd_cur->_sync = vmu_flash_sync;
mtd_cur->writesize = card->blocklen;
mpart = kmalloc(sizeof(struct mdev_part), GFP_KERNEL);
diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c
index aa7e0cb..71b0ba7 100644
--- a/drivers/mtd/maps/wr_sbc82xx_flash.c
+++ b/drivers/mtd/maps/wr_sbc82xx_flash.c
@@ -142,7 +142,7 @@ static int __init init_sbc82xx_flash(void)
nr_parts = ARRAY_SIZE(smallflash_parts);
}
- mtd_device_parse_register(sbcmtd[i], part_probes, 0,
+ mtd_device_parse_register(sbcmtd[i], part_probes, NULL,
defparts, nr_parts);
}
return 0;
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 424ca5f..f1f0671 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -233,6 +233,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
ret = __get_mtd_device(dev->mtd);
if (ret)
goto error_release;
+ dev->file_mode = mode;
unlock:
dev->open++;
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
index af65912..6c6d807 100644
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -321,8 +321,12 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd)
mutex_unlock(&mtdblk->cache_mutex);
if (!--mtdblk->count) {
- /* It was the last usage. Free the cache */
- mtd_sync(mbd->mtd);
+ /*
+ * It was the last usage. Free the cache, but only sync if
+ * opened for writing.
+ */
+ if (mbd->file_mode & FMODE_WRITE)
+ mtd_sync(mbd->mtd);
vfree(mtdblk->cache_data);
}
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index c57ae92..94eb05b 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -39,7 +39,6 @@
#include <asm/uaccess.h>
static DEFINE_MUTEX(mtd_mutex);
-static struct vfsmount *mtd_inode_mnt __read_mostly;
/*
* Data structure to hold the pointer to the mtd device as well
@@ -75,7 +74,9 @@ static loff_t mtdchar_lseek(struct file *file, loff_t offset, int orig)
return -EINVAL;
}
-
+static int count;
+static struct vfsmount *mnt;
+static struct file_system_type mtd_inodefs_type;
static int mtdchar_open(struct inode *inode, struct file *file)
{
@@ -92,6 +93,10 @@ static int mtdchar_open(struct inode *inode, struct file *file)
if ((file->f_mode & FMODE_WRITE) && (minor & 1))
return -EACCES;
+ ret = simple_pin_fs(&mtd_inodefs_type, &mnt, &count);
+ if (ret)
+ return ret;
+
mutex_lock(&mtd_mutex);
mtd = get_mtd_device(NULL, devnum);
@@ -106,7 +111,7 @@ static int mtdchar_open(struct inode *inode, struct file *file)
goto out;
}
- mtd_ino = iget_locked(mtd_inode_mnt->mnt_sb, devnum);
+ mtd_ino = iget_locked(mnt->mnt_sb, devnum);
if (!mtd_ino) {
put_mtd_device(mtd);
ret = -ENOMEM;
@@ -141,6 +146,7 @@ static int mtdchar_open(struct inode *inode, struct file *file)
out:
mutex_unlock(&mtd_mutex);
+ simple_release_fs(&mnt, &count);
return ret;
} /* mtdchar_open */
@@ -162,6 +168,7 @@ static int mtdchar_close(struct inode *inode, struct file *file)
put_mtd_device(mtd);
file->private_data = NULL;
kfree(mfi);
+ simple_release_fs(&mnt, &count);
return 0;
} /* mtdchar_close */
@@ -405,7 +412,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd,
if (length > 4096)
return -EINVAL;
- if (!mtd->write_oob)
+ if (!mtd->_write_oob)
ret = -EOPNOTSUPP;
else
ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT;
@@ -576,7 +583,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
!access_ok(VERIFY_READ, req.usr_data, req.len) ||
!access_ok(VERIFY_READ, req.usr_oob, req.ooblen))
return -EFAULT;
- if (!mtd->write_oob)
+ if (!mtd->_write_oob)
return -EOPNOTSUPP;
ops.mode = req.mode;
@@ -1175,10 +1182,15 @@ static const struct file_operations mtd_fops = {
#endif
};
+static const struct super_operations mtd_ops = {
+ .drop_inode = generic_delete_inode,
+ .statfs = simple_statfs,
+};
+
static struct dentry *mtd_inodefs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
- return mount_pseudo(fs_type, "mtd_inode:", NULL, NULL, MTD_INODE_FS_MAGIC);
+ return mount_pseudo(fs_type, "mtd_inode:", &mtd_ops, NULL, MTD_INODE_FS_MAGIC);
}
static struct file_system_type mtd_inodefs_type = {
@@ -1187,26 +1199,6 @@ static struct file_system_type mtd_inodefs_type = {
.kill_sb = kill_anon_super,
};
-static void mtdchar_notify_add(struct mtd_info *mtd)
-{
-}
-
-static void mtdchar_notify_remove(struct mtd_info *mtd)
-{
- struct inode *mtd_ino = ilookup(mtd_inode_mnt->mnt_sb, mtd->index);
-
- if (mtd_ino) {
- /* Destroy the inode if it exists */
- clear_nlink(mtd_ino);
- iput(mtd_ino);
- }
-}
-
-static struct mtd_notifier mtdchar_notifier = {
- .add = mtdchar_notify_add,
- .remove = mtdchar_notify_remove,
-};
-
static int __init init_mtdchar(void)
{
int ret;
@@ -1224,19 +1216,8 @@ static int __init init_mtdchar(void)
pr_notice("Can't register mtd_inodefs filesystem: %d\n", ret);
goto err_unregister_chdev;
}
-
- mtd_inode_mnt = kern_mount(&mtd_inodefs_type);
- if (IS_ERR(mtd_inode_mnt)) {
- ret = PTR_ERR(mtd_inode_mnt);
- pr_notice("Error mounting mtd_inodefs filesystem: %d\n", ret);
- goto err_unregister_filesystem;
- }
- register_mtd_user(&mtdchar_notifier);
-
return ret;
-err_unregister_filesystem:
- unregister_filesystem(&mtd_inodefs_type);
err_unregister_chdev:
__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
return ret;
@@ -1244,8 +1225,6 @@ err_unregister_chdev:
static void __exit cleanup_mtdchar(void)
{
- unregister_mtd_user(&mtdchar_notifier);
- kern_unmount(mtd_inode_mnt);
unregister_filesystem(&mtd_inodefs_type);
__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
}
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index 1ed5103..b900056 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -72,8 +72,6 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
int ret = 0, err;
int i;
- *retlen = 0;
-
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
size_t size, retsize;
@@ -126,11 +124,6 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
int err = -EINVAL;
int i;
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
-
- *retlen = 0;
-
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
size_t size, retsize;
@@ -145,11 +138,7 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
else
size = len;
- if (!(subdev->flags & MTD_WRITEABLE))
- err = -EROFS;
- else
- err = mtd_write(subdev, to, size, &retsize, buf);
-
+ err = mtd_write(subdev, to, size, &retsize, buf);
if (err)
break;
@@ -176,19 +165,10 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
int i;
int err = -EINVAL;
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
-
- *retlen = 0;
-
/* Calculate total length of data */
for (i = 0; i < count; i++)
total_len += vecs[i].iov_len;
- /* Do not allow write past end of device */
- if ((to + total_len) > mtd->size)
- return -EINVAL;
-
/* Check alignment */
if (mtd->writesize > 1) {
uint64_t __to = to;
@@ -224,12 +204,8 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
old_iov_len = vecs_copy[entry_high].iov_len;
vecs_copy[entry_high].iov_len = size;
- if (!(subdev->flags & MTD_WRITEABLE))
- err = -EROFS;
- else
- err = mtd_writev(subdev, &vecs_copy[entry_low],
- entry_high - entry_low + 1, to,
- &retsize);
+ err = mtd_writev(subdev, &vecs_copy[entry_low],
+ entry_high - entry_low + 1, to, &retsize);
vecs_copy[entry_high].iov_len = old_iov_len - size;
vecs_copy[entry_high].iov_base += size;
@@ -403,15 +379,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
uint64_t length, offset = 0;
struct erase_info *erase;
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
-
- if (instr->addr > concat->mtd.size)
- return -EINVAL;
-
- if (instr->len + instr->addr > concat->mtd.size)
- return -EINVAL;
-
/*
* Check for proper erase block alignment of the to-be-erased area.
* It is easier to do this based on the super device's erase
@@ -459,8 +426,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
return -EINVAL;
}
- instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
-
/* make a local copy of instr to avoid modifying the caller's struct */
erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
@@ -499,10 +464,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
else
erase->len = length;
- if (!(subdev->flags & MTD_WRITEABLE)) {
- err = -EROFS;
- break;
- }
length -= erase->len;
if ((err = concat_dev_erase(subdev, erase))) {
/* sanity check: should never happen since
@@ -538,9 +499,6 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
struct mtd_concat *concat = CONCAT(mtd);
int i, err = -EINVAL;
- if ((len + ofs) > mtd->size)
- return -EINVAL;
-
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
uint64_t size;
@@ -575,9 +533,6 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
struct mtd_concat *concat = CONCAT(mtd);
int i, err = 0;
- if ((len + ofs) > mtd->size)
- return -EINVAL;
-
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
uint64_t size;
@@ -650,9 +605,6 @@ static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
if (!mtd_can_have_bb(concat->subdev[0]))
return res;
- if (ofs > mtd->size)
- return -EINVAL;
-
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
@@ -673,12 +625,6 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
struct mtd_concat *concat = CONCAT(mtd);
int i, err = -EINVAL;
- if (!mtd_can_have_bb(concat->subdev[0]))
- return 0;
-
- if (ofs > mtd->size)
- return -EINVAL;
-
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
@@ -716,10 +662,6 @@ static unsigned long concat_get_unmapped_area(struct mtd_info *mtd,
continue;
}
- /* we've found the subdev over which the mapping will reside */
- if (offset + len > subdev->size)
- return (unsigned long) -EINVAL;
-
return mtd_get_unmapped_area(subdev, len, offset, flags);
}
@@ -777,16 +719,16 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->mtd.subpage_sft = subdev[0]->subpage_sft;
concat->mtd.oobsize = subdev[0]->oobsize;
concat->mtd.oobavail = subdev[0]->oobavail;
- if (subdev[0]->writev)
- concat->mtd.writev = concat_writev;
- if (subdev[0]->read_oob)
- concat->mtd.read_oob = concat_read_oob;
- if (subdev[0]->write_oob)
- concat->mtd.write_oob = concat_write_oob;
- if (subdev[0]->block_isbad)
- concat->mtd.block_isbad = concat_block_isbad;
- if (subdev[0]->block_markbad)
- concat->mtd.block_markbad = concat_block_markbad;
+ if (subdev[0]->_writev)
+ concat->mtd._writev = concat_writev;
+ if (subdev[0]->_read_oob)
+ concat->mtd._read_oob = concat_read_oob;
+ if (subdev[0]->_write_oob)
+ concat->mtd._write_oob = concat_write_oob;
+ if (subdev[0]->_block_isbad)
+ concat->mtd._block_isbad = concat_block_isbad;
+ if (subdev[0]->_block_markbad)
+ concat->mtd._block_markbad = concat_block_markbad;
concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
@@ -833,8 +775,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
if (concat->mtd.writesize != subdev[i]->writesize ||
concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
concat->mtd.oobsize != subdev[i]->oobsize ||
- !concat->mtd.read_oob != !subdev[i]->read_oob ||
- !concat->mtd.write_oob != !subdev[i]->write_oob) {
+ !concat->mtd._read_oob != !subdev[i]->_read_oob ||
+ !concat->mtd._write_oob != !subdev[i]->_write_oob) {
kfree(concat);
printk("Incompatible OOB or ECC data on \"%s\"\n",
subdev[i]->name);
@@ -849,15 +791,15 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->num_subdev = num_devs;
concat->mtd.name = name;
- concat->mtd.erase = concat_erase;
- concat->mtd.read = concat_read;
- concat->mtd.write = concat_write;
- concat->mtd.sync = concat_sync;
- concat->mtd.lock = concat_lock;
- concat->mtd.unlock = concat_unlock;
- concat->mtd.suspend = concat_suspend;
- concat->mtd.resume = concat_resume;
- concat->mtd.get_unmapped_area = concat_get_unmapped_area;
+ concat->mtd._erase = concat_erase;
+ concat->mtd._read = concat_read;
+ concat->mtd._write = concat_write;
+ concat->mtd._sync = concat_sync;
+ concat->mtd._lock = concat_lock;
+ concat->mtd._unlock = concat_unlock;
+ concat->mtd._suspend = concat_suspend;
+ concat->mtd._resume = concat_resume;
+ concat->mtd._get_unmapped_area = concat_get_unmapped_area;
/*
* Combine the erase block size info of the subdevices:
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 9a9ce71..c837507 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -107,7 +107,7 @@ static LIST_HEAD(mtd_notifiers);
*/
static void mtd_release(struct device *dev)
{
- struct mtd_info *mtd = dev_get_drvdata(dev);
+ struct mtd_info __maybe_unused *mtd = dev_get_drvdata(dev);
dev_t index = MTD_DEVT(mtd->index);
/* remove /dev/mtdXro node if needed */
@@ -126,7 +126,7 @@ static int mtd_cls_resume(struct device *dev)
{
struct mtd_info *mtd = dev_get_drvdata(dev);
- if (mtd && mtd->resume)
+ if (mtd)
mtd_resume(mtd);
return 0;
}
@@ -610,8 +610,8 @@ int __get_mtd_device(struct mtd_info *mtd)
if (!try_module_get(mtd->owner))
return -ENODEV;
- if (mtd->get_device) {
- err = mtd->get_device(mtd);
+ if (mtd->_get_device) {
+ err = mtd->_get_device(mtd);
if (err) {
module_put(mtd->owner);
@@ -675,14 +675,267 @@ void __put_mtd_device(struct mtd_info *mtd)
--mtd->usecount;
BUG_ON(mtd->usecount < 0);
- if (mtd->put_device)
- mtd->put_device(mtd);
+ if (mtd->_put_device)
+ mtd->_put_device(mtd);
module_put(mtd->owner);
}
EXPORT_SYMBOL_GPL(__put_mtd_device);
/*
+ * Erase is an asynchronous operation. Device drivers are supposed
+ * to call instr->callback() whenever the operation completes, even
+ * if it completes with a failure.
+ * Callers are supposed to pass a callback function and wait for it
+ * to be called before writing to the block.
+ */
+int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr)
+ return -EINVAL;
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
+ if (!instr->len) {
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+ return 0;
+ }
+ return mtd->_erase(mtd, instr);
+}
+EXPORT_SYMBOL_GPL(mtd_erase);
+
+/*
+ * This stuff for eXecute-In-Place. phys is optional and may be set to NULL.
+ */
+int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+ void **virt, resource_size_t *phys)
+{
+ *retlen = 0;
+ *virt = NULL;
+ if (phys)
+ *phys = 0;
+ if (!mtd->_point)
+ return -EOPNOTSUPP;
+ if (from < 0 || from > mtd->size || len > mtd->size - from)
+ return -EINVAL;
+ if (!len)
+ return 0;
+ return mtd->_point(mtd, from, len, retlen, virt, phys);
+}
+EXPORT_SYMBOL_GPL(mtd_point);
+
+/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
+int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+{
+ if (!mtd->_point)
+ return -EOPNOTSUPP;
+ if (from < 0 || from > mtd->size || len > mtd->size - from)
+ return -EINVAL;
+ if (!len)
+ return 0;
+ return mtd->_unpoint(mtd, from, len);
+}
+EXPORT_SYMBOL_GPL(mtd_unpoint);
+
+/*
+ * Allow NOMMU mmap() to directly map the device (if not NULL)
+ * - return the address to which the offset maps
+ * - return -ENOSYS to indicate refusal to do the mapping
+ */
+unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len,
+ unsigned long offset, unsigned long flags)
+{
+ if (!mtd->_get_unmapped_area)
+ return -EOPNOTSUPP;
+ if (offset > mtd->size || len > mtd->size - offset)
+ return -EINVAL;
+ return mtd->_get_unmapped_area(mtd, len, offset, flags);
+}
+EXPORT_SYMBOL_GPL(mtd_get_unmapped_area);
+
+int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+ u_char *buf)
+{
+ *retlen = 0;
+ if (from < 0 || from > mtd->size || len > mtd->size - from)
+ return -EINVAL;
+ if (!len)
+ return 0;
+ return mtd->_read(mtd, from, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_read);
+
+int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+ const u_char *buf)
+{
+ *retlen = 0;
+ if (to < 0 || to > mtd->size || len > mtd->size - to)
+ return -EINVAL;
+ if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (!len)
+ return 0;
+ return mtd->_write(mtd, to, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_write);
+
+/*
+ * In blackbox flight recorder like scenarios we want to make successful writes
+ * in interrupt context. panic_write() is only intended to be called when its
+ * known the kernel is about to panic and we need the write to succeed. Since
+ * the kernel is not going to be running for much longer, this function can
+ * break locks and delay to ensure the write succeeds (but not sleep).
+ */
+int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+ const u_char *buf)
+{
+ *retlen = 0;
+ if (!mtd->_panic_write)
+ return -EOPNOTSUPP;
+ if (to < 0 || to > mtd->size || len > mtd->size - to)
+ return -EINVAL;
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (!len)
+ return 0;
+ return mtd->_panic_write(mtd, to, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_panic_write);
+
+/*
+ * Method to access the protection register area, present in some flash
+ * devices. The user data is one time programmable but the factory data is read
+ * only.
+ */
+int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
+ size_t len)
+{
+ if (!mtd->_get_fact_prot_info)
+ return -EOPNOTSUPP;
+ if (!len)
+ return 0;
+ return mtd->_get_fact_prot_info(mtd, buf, len);
+}
+EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info);
+
+int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ *retlen = 0;
+ if (!mtd->_read_fact_prot_reg)
+ return -EOPNOTSUPP;
+ if (!len)
+ return 0;
+ return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg);
+
+int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
+ size_t len)
+{
+ if (!mtd->_get_user_prot_info)
+ return -EOPNOTSUPP;
+ if (!len)
+ return 0;
+ return mtd->_get_user_prot_info(mtd, buf, len);
+}
+EXPORT_SYMBOL_GPL(mtd_get_user_prot_info);
+
+int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ *retlen = 0;
+ if (!mtd->_read_user_prot_reg)
+ return -EOPNOTSUPP;
+ if (!len)
+ return 0;
+ return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg);
+
+int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ *retlen = 0;
+ if (!mtd->_write_user_prot_reg)
+ return -EOPNOTSUPP;
+ if (!len)
+ return 0;
+ return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
+}
+EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg);
+
+int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len)
+{
+ if (!mtd->_lock_user_prot_reg)
+ return -EOPNOTSUPP;
+ if (!len)
+ return 0;
+ return mtd->_lock_user_prot_reg(mtd, from, len);
+}
+EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg);
+
+/* Chip-supported device locking */
+int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+ if (!mtd->_lock)
+ return -EOPNOTSUPP;
+ if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
+ return -EINVAL;
+ if (!len)
+ return 0;
+ return mtd->_lock(mtd, ofs, len);
+}
+EXPORT_SYMBOL_GPL(mtd_lock);
+
+int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+ if (!mtd->_unlock)
+ return -EOPNOTSUPP;
+ if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
+ return -EINVAL;
+ if (!len)
+ return 0;
+ return mtd->_unlock(mtd, ofs, len);
+}
+EXPORT_SYMBOL_GPL(mtd_unlock);
+
+int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+ if (!mtd->_is_locked)
+ return -EOPNOTSUPP;
+ if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs)
+ return -EINVAL;
+ if (!len)
+ return 0;
+ return mtd->_is_locked(mtd, ofs, len);
+}
+EXPORT_SYMBOL_GPL(mtd_is_locked);
+
+int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+ if (!mtd->_block_isbad)
+ return 0;
+ if (ofs < 0 || ofs > mtd->size)
+ return -EINVAL;
+ return mtd->_block_isbad(mtd, ofs);
+}
+EXPORT_SYMBOL_GPL(mtd_block_isbad);
+
+int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ if (!mtd->_block_markbad)
+ return -EOPNOTSUPP;
+ if (ofs < 0 || ofs > mtd->size)
+ return -EINVAL;
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ return mtd->_block_markbad(mtd, ofs);
+}
+EXPORT_SYMBOL_GPL(mtd_block_markbad);
+
+/*
* default_mtd_writev - the default writev method
* @mtd: mtd device description object pointer
* @vecs: the vectors to write
@@ -729,9 +982,11 @@ int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen)
{
*retlen = 0;
- if (!mtd->writev)
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (!mtd->_writev)
return default_mtd_writev(mtd, vecs, count, to, retlen);
- return mtd->writev(mtd, vecs, count, to, retlen);
+ return mtd->_writev(mtd, vecs, count, to, retlen);
}
EXPORT_SYMBOL_GPL(mtd_writev);
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
index 3ce99e0..ae36d7e 100644
--- a/drivers/mtd/mtdoops.c
+++ b/drivers/mtd/mtdoops.c
@@ -169,7 +169,7 @@ static void mtdoops_workfunc_erase(struct work_struct *work)
cxt->nextpage = 0;
}
- while (mtd_can_have_bb(mtd)) {
+ while (1) {
ret = mtd_block_isbad(mtd, cxt->nextpage * record_size);
if (!ret)
break;
@@ -199,9 +199,9 @@ badblock:
return;
}
- if (mtd_can_have_bb(mtd) && ret == -EIO) {
+ if (ret == -EIO) {
ret = mtd_block_markbad(mtd, cxt->nextpage * record_size);
- if (ret < 0) {
+ if (ret < 0 && ret != -EOPNOTSUPP) {
printk(KERN_ERR "mtdoops: block_markbad failed, aborting\n");
return;
}
@@ -257,8 +257,7 @@ static void find_next_position(struct mtdoops_context *cxt)
size_t retlen;
for (page = 0; page < cxt->oops_pages; page++) {
- if (mtd_can_have_bb(mtd) &&
- mtd_block_isbad(mtd, page * record_size))
+ if (mtd_block_isbad(mtd, page * record_size))
continue;
/* Assume the page is used */
mark_page_used(cxt, page);
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index a3d44c3..9651c06 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -65,12 +65,8 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
int res;
stats = part->master->ecc_stats;
-
- if (from >= mtd->size)
- len = 0;
- else if (from + len > mtd->size)
- len = mtd->size - from;
- res = mtd_read(part->master, from + part->offset, len, retlen, buf);
+ res = part->master->_read(part->master, from + part->offset, len,
+ retlen, buf);
if (unlikely(res)) {
if (mtd_is_bitflip(res))
mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
@@ -84,19 +80,16 @@ static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys)
{
struct mtd_part *part = PART(mtd);
- if (from >= mtd->size)
- len = 0;
- else if (from + len > mtd->size)
- len = mtd->size - from;
- return mtd_point(part->master, from + part->offset, len, retlen,
- virt, phys);
+
+ return part->master->_point(part->master, from + part->offset, len,
+ retlen, virt, phys);
}
-static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
struct mtd_part *part = PART(mtd);
- mtd_unpoint(part->master, from + part->offset, len);
+ return part->master->_unpoint(part->master, from + part->offset, len);
}
static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
@@ -107,7 +100,8 @@ static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
struct mtd_part *part = PART(mtd);
offset += part->offset;
- return mtd_get_unmapped_area(part->master, len, offset, flags);
+ return part->master->_get_unmapped_area(part->master, len, offset,
+ flags);
}
static int part_read_oob(struct mtd_info *mtd, loff_t from,
@@ -138,7 +132,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
return -EINVAL;
}
- res = mtd_read_oob(part->master, from + part->offset, ops);
+ res = part->master->_read_oob(part->master, from + part->offset, ops);
if (unlikely(res)) {
if (mtd_is_bitflip(res))
mtd->ecc_stats.corrected++;
@@ -152,55 +146,46 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
struct mtd_part *part = PART(mtd);
- return mtd_read_user_prot_reg(part->master, from, len, retlen, buf);
+ return part->master->_read_user_prot_reg(part->master, from, len,
+ retlen, buf);
}
static int part_get_user_prot_info(struct mtd_info *mtd,
struct otp_info *buf, size_t len)
{
struct mtd_part *part = PART(mtd);
- return mtd_get_user_prot_info(part->master, buf, len);
+ return part->master->_get_user_prot_info(part->master, buf, len);
}
static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
struct mtd_part *part = PART(mtd);
- return mtd_read_fact_prot_reg(part->master, from, len, retlen, buf);
+ return part->master->_read_fact_prot_reg(part->master, from, len,
+ retlen, buf);
}
static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
size_t len)
{
struct mtd_part *part = PART(mtd);
- return mtd_get_fact_prot_info(part->master, buf, len);
+ return part->master->_get_fact_prot_info(part->master, buf, len);
}
static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
- if (to >= mtd->size)
- len = 0;
- else if (to + len > mtd->size)
- len = mtd->size - to;
- return mtd_write(part->master, to + part->offset, len, retlen, buf);
+ return part->master->_write(part->master, to + part->offset, len,
+ retlen, buf);
}
static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
- if (to >= mtd->size)
- len = 0;
- else if (to + len > mtd->size)
- len = mtd->size - to;
- return mtd_panic_write(part->master, to + part->offset, len, retlen,
- buf);
+ return part->master->_panic_write(part->master, to + part->offset, len,
+ retlen, buf);
}
static int part_write_oob(struct mtd_info *mtd, loff_t to,
@@ -208,50 +193,43 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to,
{
struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
-
if (to >= mtd->size)
return -EINVAL;
if (ops->datbuf && to + ops->len > mtd->size)
return -EINVAL;
- return mtd_write_oob(part->master, to + part->offset, ops);
+ return part->master->_write_oob(part->master, to + part->offset, ops);
}
static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
struct mtd_part *part = PART(mtd);
- return mtd_write_user_prot_reg(part->master, from, len, retlen, buf);
+ return part->master->_write_user_prot_reg(part->master, from, len,
+ retlen, buf);
}
static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len)
{
struct mtd_part *part = PART(mtd);
- return mtd_lock_user_prot_reg(part->master, from, len);
+ return part->master->_lock_user_prot_reg(part->master, from, len);
}
static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen)
{
struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
- return mtd_writev(part->master, vecs, count, to + part->offset,
- retlen);
+ return part->master->_writev(part->master, vecs, count,
+ to + part->offset, retlen);
}
static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct mtd_part *part = PART(mtd);
int ret;
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
- if (instr->addr >= mtd->size)
- return -EINVAL;
+
instr->addr += part->offset;
- ret = mtd_erase(part->master, instr);
+ ret = part->master->_erase(part->master, instr);
if (ret) {
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
instr->fail_addr -= part->offset;
@@ -262,7 +240,7 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
void mtd_erase_callback(struct erase_info *instr)
{
- if (instr->mtd->erase == part_erase) {
+ if (instr->mtd->_erase == part_erase) {
struct mtd_part *part = PART(instr->mtd);
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
@@ -277,52 +255,44 @@ EXPORT_SYMBOL_GPL(mtd_erase_callback);
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_part *part = PART(mtd);
- if ((len + ofs) > mtd->size)
- return -EINVAL;
- return mtd_lock(part->master, ofs + part->offset, len);
+ return part->master->_lock(part->master, ofs + part->offset, len);
}
static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_part *part = PART(mtd);
- if ((len + ofs) > mtd->size)
- return -EINVAL;
- return mtd_unlock(part->master, ofs + part->offset, len);
+ return part->master->_unlock(part->master, ofs + part->offset, len);
}
static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_part *part = PART(mtd);
- if ((len + ofs) > mtd->size)
- return -EINVAL;
- return mtd_is_locked(part->master, ofs + part->offset, len);
+ return part->master->_is_locked(part->master, ofs + part->offset, len);
}
static void part_sync(struct mtd_info *mtd)
{
struct mtd_part *part = PART(mtd);
- mtd_sync(part->master);
+ part->master->_sync(part->master);
}
static int part_suspend(struct mtd_info *mtd)
{
struct mtd_part *part = PART(mtd);
- return mtd_suspend(part->master);
+ return part->master->_suspend(part->master);
}
static void part_resume(struct mtd_info *mtd)
{
struct mtd_part *part = PART(mtd);
- mtd_resume(part->master);
+ part->master->_resume(part->master);
}
static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
{
struct mtd_part *part = PART(mtd);
- if (ofs >= mtd->size)
- return -EINVAL;
ofs += part->offset;
- return mtd_block_isbad(part->master, ofs);
+ return part->master->_block_isbad(part->master, ofs);
}
static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
@@ -330,12 +300,8 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
struct mtd_part *part = PART(mtd);
int res;
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
- if (ofs >= mtd->size)
- return -EINVAL;
ofs += part->offset;
- res = mtd_block_markbad(part->master, ofs);
+ res = part->master->_block_markbad(part->master, ofs);
if (!res)
mtd->ecc_stats.badblocks++;
return res;
@@ -410,54 +376,55 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
*/
slave->mtd.dev.parent = master->dev.parent;
- slave->mtd.read = part_read;
- slave->mtd.write = part_write;
+ slave->mtd._read = part_read;
+ slave->mtd._write = part_write;
- if (master->panic_write)
- slave->mtd.panic_write = part_panic_write;
+ if (master->_panic_write)
+ slave->mtd._panic_write = part_panic_write;
- if (master->point && master->unpoint) {
- slave->mtd.point = part_point;
- slave->mtd.unpoint = part_unpoint;
+ if (master->_point && master->_unpoint) {
+ slave->mtd._point = part_point;
+ slave->mtd._unpoint = part_unpoint;
}
- if (master->get_unmapped_area)
- slave->mtd.get_unmapped_area = part_get_unmapped_area;
- if (master->read_oob)
- slave->mtd.read_oob = part_read_oob;
- if (master->write_oob)
- slave->mtd.write_oob = part_write_oob;
- if (master->read_user_prot_reg)
- slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
- if (master->read_fact_prot_reg)
- slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
- if (master->write_user_prot_reg)
- slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
- if (master->lock_user_prot_reg)
- slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
- if (master->get_user_prot_info)
- slave->mtd.get_user_prot_info = part_get_user_prot_info;
- if (master->get_fact_prot_info)
- slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
- if (master->sync)
- slave->mtd.sync = part_sync;
- if (!partno && !master->dev.class && master->suspend && master->resume) {
- slave->mtd.suspend = part_suspend;
- slave->mtd.resume = part_resume;
+ if (master->_get_unmapped_area)
+ slave->mtd._get_unmapped_area = part_get_unmapped_area;
+ if (master->_read_oob)
+ slave->mtd._read_oob = part_read_oob;
+ if (master->_write_oob)
+ slave->mtd._write_oob = part_write_oob;
+ if (master->_read_user_prot_reg)
+ slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
+ if (master->_read_fact_prot_reg)
+ slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
+ if (master->_write_user_prot_reg)
+ slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
+ if (master->_lock_user_prot_reg)
+ slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
+ if (master->_get_user_prot_info)
+ slave->mtd._get_user_prot_info = part_get_user_prot_info;
+ if (master->_get_fact_prot_info)
+ slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
+ if (master->_sync)
+ slave->mtd._sync = part_sync;
+ if (!partno && !master->dev.class && master->_suspend &&
+ master->_resume) {
+ slave->mtd._suspend = part_suspend;
+ slave->mtd._resume = part_resume;
}
- if (master->writev)
- slave->mtd.writev = part_writev;
- if (master->lock)
- slave->mtd.lock = part_lock;
- if (master->unlock)
- slave->mtd.unlock = part_unlock;
- if (master->is_locked)
- slave->mtd.is_locked = part_is_locked;
- if (master->block_isbad)
- slave->mtd.block_isbad = part_block_isbad;
- if (master->block_markbad)
- slave->mtd.block_markbad = part_block_markbad;
- slave->mtd.erase = part_erase;
+ if (master->_writev)
+ slave->mtd._writev = part_writev;
+ if (master->_lock)
+ slave->mtd._lock = part_lock;
+ if (master->_unlock)
+ slave->mtd._unlock = part_unlock;
+ if (master->_is_locked)
+ slave->mtd._is_locked = part_is_locked;
+ if (master->_block_isbad)
+ slave->mtd._block_isbad = part_block_isbad;
+ if (master->_block_markbad)
+ slave->mtd._block_markbad = part_block_markbad;
+ slave->mtd._erase = part_erase;
slave->master = master;
slave->offset = part->offset;
@@ -549,7 +516,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
}
slave->mtd.ecclayout = master->ecclayout;
- if (master->block_isbad) {
+ slave->mtd.ecc_strength = master->ecc_strength;
+ if (master->_block_isbad) {
uint64_t offs = 0;
while (offs < slave->mtd.size) {
@@ -761,7 +729,7 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types,
for ( ; ret <= 0 && *types; types++) {
parser = get_partition_parser(*types);
if (!parser && !request_module("%s", *types))
- parser = get_partition_parser(*types);
+ parser = get_partition_parser(*types);
if (!parser)
continue;
ret = (*parser->parse_fn)(master, pparts, data);
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 3d8d2d8..7d17cec 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -246,6 +246,7 @@ config MTD_NAND_BCM_UMI_HWCS
config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
depends on EXPERIMENTAL
+ depends on HAS_IOMEM
select REED_SOLOMON
select REED_SOLOMON_DEC16
help
@@ -313,6 +314,26 @@ config MTD_NAND_DISKONCHIP_BBTWRITE
load time (assuming you build diskonchip as a module) with the module
parameter "inftl_bbt_write=1".
+config MTD_NAND_DOCG4
+ tristate "Support for DiskOnChip G4 (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ select BCH
+ select BITREVERSE
+ help
+ Support for diskonchip G4 nand flash, found in various smartphones and
+ PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba
+ Portege G900, Asus P526, and O2 XDA Zinc.
+
+ With this driver you will be able to use UBI and create a ubifs on the
+ device, so you may wish to consider enabling UBI and UBIFS as well.
+
+ These devices ship with the Mys/Sandisk SAFTL formatting, for which
+ there is currently no mtd parser, so you may want to use command line
+ partitioning to segregate write-protected blocks. On the Treo680, the
+ first five erase blocks (256KiB each) are write-protected, followed
+ by the block containing the saftl partition table. This is probably
+ typical.
+
config MTD_NAND_SHARPSL
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
depends on ARCH_PXA
@@ -420,7 +441,6 @@ config MTD_NAND_NANDSIM
config MTD_NAND_GPMI_NAND
bool "GPMI NAND Flash Controller driver"
depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28)
- select MTD_CMDLINE_PARTS
help
Enables NAND Flash support for IMX23 or IMX28.
The GPMI controller is very powerful, with the help of BCH
@@ -431,6 +451,7 @@ config MTD_NAND_GPMI_NAND
config MTD_NAND_PLATFORM
tristate "Support for generic platform NAND driver"
+ depends on HAS_IOMEM
help
This implements a generic NAND driver for on-SOC platform
devices. You will need to provide platform-specific functions
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 19bc8cb..d4b4d87 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
+obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o
obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
obj-$(CONFIG_MTD_NAND_H1900) += h1910.o
obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c
index 6a5ff64..4f20e1d 100644
--- a/drivers/mtd/nand/alauda.c
+++ b/drivers/mtd/nand/alauda.c
@@ -585,12 +585,13 @@ static int alauda_init_media(struct alauda *al)
mtd->writesize = 1<<card->pageshift;
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
- mtd->read = alauda_read;
- mtd->write = alauda_write;
- mtd->erase = alauda_erase;
- mtd->block_isbad = alauda_isbad;
+ mtd->_read = alauda_read;
+ mtd->_write = alauda_write;
+ mtd->_erase = alauda_erase;
+ mtd->_block_isbad = alauda_isbad;
mtd->priv = al;
mtd->owner = THIS_MODULE;
+ mtd->ecc_strength = 1;
err = mtd_device_register(mtd, NULL, 0);
if (err) {
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 35b4fb5..2165576 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -27,6 +27,10 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_mtd.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
@@ -34,22 +38,10 @@
#include <linux/dmaengine.h>
#include <linux/gpio.h>
#include <linux/io.h>
+#include <linux/platform_data/atmel.h>
-#include <mach/board.h>
#include <mach/cpu.h>
-#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW
-#define hard_ecc 1
-#else
-#define hard_ecc 0
-#endif
-
-#ifdef CONFIG_MTD_NAND_ATMEL_ECC_NONE
-#define no_ecc 1
-#else
-#define no_ecc 0
-#endif
-
static int use_dma = 1;
module_param(use_dma, int, 0);
@@ -95,7 +87,7 @@ struct atmel_nand_host {
struct mtd_info mtd;
void __iomem *io_base;
dma_addr_t io_phys;
- struct atmel_nand_data *board;
+ struct atmel_nand_data board;
struct device *dev;
void __iomem *ecc;
@@ -113,8 +105,8 @@ static int cpu_has_dma(void)
*/
static void atmel_nand_enable(struct atmel_nand_host *host)
{
- if (gpio_is_valid(host->board->enable_pin))
- gpio_set_value(host->board->enable_pin, 0);
+ if (gpio_is_valid(host->board.enable_pin))
+ gpio_set_value(host->board.enable_pin, 0);
}
/*
@@ -122,8 +114,8 @@ static void atmel_nand_enable(struct atmel_nand_host *host)
*/
static void atmel_nand_disable(struct atmel_nand_host *host)
{
- if (gpio_is_valid(host->board->enable_pin))
- gpio_set_value(host->board->enable_pin, 1);
+ if (gpio_is_valid(host->board.enable_pin))
+ gpio_set_value(host->board.enable_pin, 1);
}
/*
@@ -144,9 +136,9 @@ static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl
return;
if (ctrl & NAND_CLE)
- writeb(cmd, host->io_base + (1 << host->board->cle));
+ writeb(cmd, host->io_base + (1 << host->board.cle));
else
- writeb(cmd, host->io_base + (1 << host->board->ale));
+ writeb(cmd, host->io_base + (1 << host->board.ale));
}
/*
@@ -157,8 +149,8 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
struct nand_chip *nand_chip = mtd->priv;
struct atmel_nand_host *host = nand_chip->priv;
- return gpio_get_value(host->board->rdy_pin) ^
- !!host->board->rdy_pin_active_low;
+ return gpio_get_value(host->board.rdy_pin) ^
+ !!host->board.rdy_pin_active_low;
}
/*
@@ -273,7 +265,7 @@ static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
return;
- if (host->board->bus_width_16)
+ if (host->board.bus_width_16)
atmel_read_buf16(mtd, buf, len);
else
atmel_read_buf8(mtd, buf, len);
@@ -289,7 +281,7 @@ static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
return;
- if (host->board->bus_width_16)
+ if (host->board.bus_width_16)
atmel_write_buf16(mtd, buf, len);
else
atmel_write_buf8(mtd, buf, len);
@@ -481,6 +473,56 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
}
}
+#if defined(CONFIG_OF)
+static int __devinit atmel_of_init_port(struct atmel_nand_host *host,
+ struct device_node *np)
+{
+ u32 val;
+ int ecc_mode;
+ struct atmel_nand_data *board = &host->board;
+ enum of_gpio_flags flags;
+
+ if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) {
+ if (val >= 32) {
+ dev_err(host->dev, "invalid addr-offset %u\n", val);
+ return -EINVAL;
+ }
+ board->ale = val;
+ }
+
+ if (of_property_read_u32(np, "atmel,nand-cmd-offset", &val) == 0) {
+ if (val >= 32) {
+ dev_err(host->dev, "invalid cmd-offset %u\n", val);
+ return -EINVAL;
+ }
+ board->cle = val;
+ }
+
+ ecc_mode = of_get_nand_ecc_mode(np);
+
+ board->ecc_mode = ecc_mode < 0 ? NAND_ECC_SOFT : ecc_mode;
+
+ board->on_flash_bbt = of_get_nand_on_flash_bbt(np);
+
+ if (of_get_nand_bus_width(np) == 16)
+ board->bus_width_16 = 1;
+
+ board->rdy_pin = of_get_gpio_flags(np, 0, &flags);
+ board->rdy_pin_active_low = (flags == OF_GPIO_ACTIVE_LOW);
+
+ board->enable_pin = of_get_gpio(np, 1);
+ board->det_pin = of_get_gpio(np, 2);
+
+ return 0;
+}
+#else
+static int __devinit atmel_of_init_port(struct atmel_nand_host *host,
+ struct device_node *np)
+{
+ return -EINVAL;
+}
+#endif
+
/*
* Probe for the NAND device.
*/
@@ -491,6 +533,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
struct nand_chip *nand_chip;
struct resource *regs;
struct resource *mem;
+ struct mtd_part_parser_data ppdata = {};
int res;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -517,8 +560,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
mtd = &host->mtd;
nand_chip = &host->nand_chip;
- host->board = pdev->dev.platform_data;
host->dev = &pdev->dev;
+ if (pdev->dev.of_node) {
+ res = atmel_of_init_port(host, pdev->dev.of_node);
+ if (res)
+ goto err_nand_ioremap;
+ } else {
+ memcpy(&host->board, pdev->dev.platform_data,
+ sizeof(struct atmel_nand_data));
+ }
nand_chip->priv = host; /* link the private data structures */
mtd->priv = nand_chip;
@@ -529,36 +579,36 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->IO_ADDR_W = host->io_base;
nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl;
- if (gpio_is_valid(host->board->rdy_pin))
+ if (gpio_is_valid(host->board.rdy_pin))
nand_chip->dev_ready = atmel_nand_device_ready;
+ nand_chip->ecc.mode = host->board.ecc_mode;
+
regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!regs && hard_ecc) {
+ if (!regs && nand_chip->ecc.mode == NAND_ECC_HW) {
printk(KERN_ERR "atmel_nand: can't get I/O resource "
"regs\nFalling back on software ECC\n");
+ nand_chip->ecc.mode = NAND_ECC_SOFT;
}
- nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */
- if (no_ecc)
- nand_chip->ecc.mode = NAND_ECC_NONE;
- if (hard_ecc && regs) {
+ if (nand_chip->ecc.mode == NAND_ECC_HW) {
host->ecc = ioremap(regs->start, resource_size(regs));
if (host->ecc == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
res = -EIO;
goto err_ecc_ioremap;
}
- nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.calculate = atmel_nand_calculate;
nand_chip->ecc.correct = atmel_nand_correct;
nand_chip->ecc.hwctl = atmel_nand_hwctl;
nand_chip->ecc.read_page = atmel_nand_read_page;
nand_chip->ecc.bytes = 4;
+ nand_chip->ecc.strength = 1;
}
nand_chip->chip_delay = 20; /* 20us command delay time */
- if (host->board->bus_width_16) /* 16-bit bus width */
+ if (host->board.bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
nand_chip->read_buf = atmel_read_buf;
@@ -567,15 +617,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
atmel_nand_enable(host);
- if (gpio_is_valid(host->board->det_pin)) {
- if (gpio_get_value(host->board->det_pin)) {
+ if (gpio_is_valid(host->board.det_pin)) {
+ if (gpio_get_value(host->board.det_pin)) {
printk(KERN_INFO "No SmartMedia card inserted.\n");
res = -ENXIO;
goto err_no_card;
}
}
- if (on_flash_bbt) {
+ if (host->board.on_flash_bbt || on_flash_bbt) {
printk(KERN_INFO "atmel_nand: Use On Flash BBT\n");
nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
}
@@ -650,8 +700,9 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
}
mtd->name = "atmel_nand";
- res = mtd_device_parse_register(mtd, NULL, 0,
- host->board->parts, host->board->num_parts);
+ ppdata.of_node = pdev->dev.of_node;
+ res = mtd_device_parse_register(mtd, NULL, &ppdata,
+ host->board.parts, host->board.num_parts);
if (!res)
return res;
@@ -695,11 +746,21 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id atmel_nand_dt_ids[] = {
+ { .compatible = "atmel,at91rm9200-nand" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_nand_dt_ids);
+#endif
+
static struct platform_driver atmel_nand_driver = {
.remove = __exit_p(atmel_nand_remove),
.driver = {
.name = "atmel_nand",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(atmel_nand_dt_ids),
},
};
diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c
index 50387fd..6908cdd 100644
--- a/drivers/mtd/nand/bcm_umi_nand.c
+++ b/drivers/mtd/nand/bcm_umi_nand.c
@@ -31,7 +31,6 @@
#include <linux/mtd/partitions.h>
#include <asm/mach-types.h>
-#include <asm/system.h>
#include <mach/reg_nand.h>
#include <mach/reg_umi.h>
@@ -476,6 +475,14 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
largepage_bbt.options = NAND_BBT_SCAN2NDPAGE;
this->badblock_pattern = &largepage_bbt;
}
+
+ /*
+ * FIXME: ecc strength value of 6 bits per 512 bytes of data is a
+ * conservative guess, given 13 ecc bytes and using bch alg.
+ * (Assume Galois field order m=15 to allow a margin of error.)
+ */
+ this->ecc.strength = 6;
+
#endif
/* Now finish off the scan, now that ecc.layout has been initialized. */
@@ -488,7 +495,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
/* Register the partitions */
board_mtd->name = "bcm_umi-nand";
- mtd_device_parse_register(board_mtd, NULL, 0, NULL, 0);
+ mtd_device_parse_register(board_mtd, NULL, NULL, NULL, 0);
/* Return happy */
return 0;
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
index dd899cb..d7b86b9 100644
--- a/drivers/mtd/nand/bf5xx_nand.c
+++ b/drivers/mtd/nand/bf5xx_nand.c
@@ -702,9 +702,11 @@ static int bf5xx_nand_scan(struct mtd_info *mtd)
if (likely(mtd->writesize >= 512)) {
chip->ecc.size = 512;
chip->ecc.bytes = 6;
+ chip->ecc.strength = 2;
} else {
chip->ecc.size = 256;
chip->ecc.bytes = 3;
+ chip->ecc.strength = 1;
bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET));
SSYNC();
}
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
index 72d3f23..2a96e1a 100644
--- a/drivers/mtd/nand/cafe_nand.c
+++ b/drivers/mtd/nand/cafe_nand.c
@@ -783,6 +783,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
cafe->nand.ecc.size = mtd->writesize;
cafe->nand.ecc.bytes = 14;
+ cafe->nand.ecc.strength = 4;
cafe->nand.ecc.hwctl = (void *)cafe_nand_bug;
cafe->nand.ecc.calculate = (void *)cafe_nand_bug;
cafe->nand.ecc.correct = (void *)cafe_nand_bug;
@@ -799,7 +800,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, mtd);
mtd->name = "cafe_nand";
- mtd_device_parse_register(mtd, part_probes, 0, NULL, 0);
+ mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
goto out;
diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c
index 737ef9a..1024bfc 100644
--- a/drivers/mtd/nand/cmx270_nand.c
+++ b/drivers/mtd/nand/cmx270_nand.c
@@ -219,7 +219,7 @@ static int __init cmx270_init(void)
}
/* Register the partitions */
- ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, 0,
+ ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL,
partition_info, NUM_PARTITIONS);
if (ret)
goto err_scan;
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c
index 414afa7..821c34c 100644
--- a/drivers/mtd/nand/cs553x_nand.c
+++ b/drivers/mtd/nand/cs553x_nand.c
@@ -248,6 +248,8 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
goto out_ior;
}
+ this->ecc.strength = 1;
+
new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
cs553x_mtd[cs] = new_mtd;
@@ -313,7 +315,7 @@ static int __init cs553x_init(void)
for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
if (cs553x_mtd[i]) {
/* If any devices registered, return success. Else the last error. */
- mtd_device_parse_register(cs553x_mtd[i], NULL, 0,
+ mtd_device_parse_register(cs553x_mtd[i], NULL, NULL,
NULL, 0);
err = 0;
}
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index 6e56615..d94b03c 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -641,6 +641,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
info->chip.ecc.bytes = 3;
}
info->chip.ecc.size = 512;
+ info->chip.ecc.strength = pdata->ecc_bits;
break;
default:
ret = -EINVAL;
@@ -752,8 +753,8 @@ syndrome_done:
if (ret < 0)
goto err_scan;
- ret = mtd_device_parse_register(&info->mtd, NULL, 0,
- pdata->parts, pdata->nr_parts);
+ ret = mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts,
+ pdata->nr_parts);
if (ret < 0)
goto err_scan;
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 3984d48..a9e57d6 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -1590,6 +1590,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
ECC_15BITS * (denali->mtd.writesize /
ECC_SECTOR_SIZE)))) {
/* if MLC OOB size is large enough, use 15bit ECC*/
+ denali->nand.ecc.strength = 15;
denali->nand.ecc.layout = &nand_15bit_oob;
denali->nand.ecc.bytes = ECC_15BITS;
iowrite32(15, denali->flash_reg + ECC_CORRECTION);
@@ -1600,12 +1601,14 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
" contain 8bit ECC correction codes");
goto failed_req_irq;
} else {
+ denali->nand.ecc.strength = 8;
denali->nand.ecc.layout = &nand_8bit_oob;
denali->nand.ecc.bytes = ECC_8BITS;
iowrite32(8, denali->flash_reg + ECC_CORRECTION);
}
denali->nand.ecc.bytes *= denali->devnum;
+ denali->nand.ecc.strength *= denali->devnum;
denali->nand.ecc.layout->eccbytes *=
denali->mtd.writesize / ECC_SECTOR_SIZE;
denali->nand.ecc.layout->oobfree[0].offset =
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index df921e7..e2ca067 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -1653,6 +1653,7 @@ static int __init doc_probe(unsigned long physadr)
nand->ecc.mode = NAND_ECC_HW_SYNDROME;
nand->ecc.size = 512;
nand->ecc.bytes = 6;
+ nand->ecc.strength = 2;
nand->bbt_options = NAND_BBT_USE_FLASH;
doc->physadr = physadr;
diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c
new file mode 100644
index 0000000..b082026
--- /dev/null
+++ b/drivers/mtd/nand/docg4.c
@@ -0,0 +1,1377 @@
+/*
+ * Copyright © 2012 Mike Dunn <mikedunn@newsguy.com>
+ *
+ * mtd nand driver for M-Systems DiskOnChip G4
+ *
+ * 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.
+ *
+ * Tested on the Palm Treo 680. The G4 is also present on Toshiba Portege, Asus
+ * P526, some HTC smartphones (Wizard, Prophet, ...), O2 XDA Zinc, maybe others.
+ * Should work on these as well. Let me know!
+ *
+ * TODO:
+ *
+ * Mechanism for management of password-protected areas
+ *
+ * Hamming ecc when reading oob only
+ *
+ * According to the M-Sys documentation, this device is also available in a
+ * "dual-die" configuration having a 256MB capacity, but no mechanism for
+ * detecting this variant is documented. Currently this driver assumes 128MB
+ * capacity.
+ *
+ * Support for multiple cascaded devices ("floors"). Not sure which gadgets
+ * contain multiple G4s in a cascaded configuration, if any.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/bch.h>
+#include <linux/bitrev.h>
+
+/*
+ * You'll want to ignore badblocks if you're reading a partition that contains
+ * data written by the TrueFFS library (i.e., by PalmOS, Windows, etc), since
+ * it does not use mtd nand's method for marking bad blocks (using oob area).
+ * This will also skip the check of the "page written" flag.
+ */
+static bool ignore_badblocks;
+module_param(ignore_badblocks, bool, 0);
+MODULE_PARM_DESC(ignore_badblocks, "no badblock checking performed");
+
+struct docg4_priv {
+ struct mtd_info *mtd;
+ struct device *dev;
+ void __iomem *virtadr;
+ int status;
+ struct {
+ unsigned int command;
+ int column;
+ int page;
+ } last_command;
+ uint8_t oob_buf[16];
+ uint8_t ecc_buf[7];
+ int oob_page;
+ struct bch_control *bch;
+};
+
+/*
+ * Defines prefixed with DOCG4 are unique to the diskonchip G4. All others are
+ * shared with other diskonchip devices (P3, G3 at least).
+ *
+ * Functions with names prefixed with docg4_ are mtd / nand interface functions
+ * (though they may also be called internally). All others are internal.
+ */
+
+#define DOC_IOSPACE_DATA 0x0800
+
+/* register offsets */
+#define DOC_CHIPID 0x1000
+#define DOC_DEVICESELECT 0x100a
+#define DOC_ASICMODE 0x100c
+#define DOC_DATAEND 0x101e
+#define DOC_NOP 0x103e
+
+#define DOC_FLASHSEQUENCE 0x1032
+#define DOC_FLASHCOMMAND 0x1034
+#define DOC_FLASHADDRESS 0x1036
+#define DOC_FLASHCONTROL 0x1038
+#define DOC_ECCCONF0 0x1040
+#define DOC_ECCCONF1 0x1042
+#define DOC_HAMMINGPARITY 0x1046
+#define DOC_BCH_SYNDROM(idx) (0x1048 + idx)
+
+#define DOC_ASICMODECONFIRM 0x1072
+#define DOC_CHIPID_INV 0x1074
+#define DOC_POWERMODE 0x107c
+
+#define DOCG4_MYSTERY_REG 0x1050
+
+/* apparently used only to write oob bytes 6 and 7 */
+#define DOCG4_OOB_6_7 0x1052
+
+/* DOC_FLASHSEQUENCE register commands */
+#define DOC_SEQ_RESET 0x00
+#define DOCG4_SEQ_PAGE_READ 0x03
+#define DOCG4_SEQ_FLUSH 0x29
+#define DOCG4_SEQ_PAGEWRITE 0x16
+#define DOCG4_SEQ_PAGEPROG 0x1e
+#define DOCG4_SEQ_BLOCKERASE 0x24
+
+/* DOC_FLASHCOMMAND register commands */
+#define DOCG4_CMD_PAGE_READ 0x00
+#define DOC_CMD_ERASECYCLE2 0xd0
+#define DOCG4_CMD_FLUSH 0x70
+#define DOCG4_CMD_READ2 0x30
+#define DOC_CMD_PROG_BLOCK_ADDR 0x60
+#define DOCG4_CMD_PAGEWRITE 0x80
+#define DOC_CMD_PROG_CYCLE2 0x10
+#define DOC_CMD_RESET 0xff
+
+/* DOC_POWERMODE register bits */
+#define DOC_POWERDOWN_READY 0x80
+
+/* DOC_FLASHCONTROL register bits */
+#define DOC_CTRL_CE 0x10
+#define DOC_CTRL_UNKNOWN 0x40
+#define DOC_CTRL_FLASHREADY 0x01
+
+/* DOC_ECCCONF0 register bits */
+#define DOC_ECCCONF0_READ_MODE 0x8000
+#define DOC_ECCCONF0_UNKNOWN 0x2000
+#define DOC_ECCCONF0_ECC_ENABLE 0x1000
+#define DOC_ECCCONF0_DATA_BYTES_MASK 0x07ff
+
+/* DOC_ECCCONF1 register bits */
+#define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80
+#define DOC_ECCCONF1_ECC_ENABLE 0x07
+#define DOC_ECCCONF1_PAGE_IS_WRITTEN 0x20
+
+/* DOC_ASICMODE register bits */
+#define DOC_ASICMODE_RESET 0x00
+#define DOC_ASICMODE_NORMAL 0x01
+#define DOC_ASICMODE_POWERDOWN 0x02
+#define DOC_ASICMODE_MDWREN 0x04
+#define DOC_ASICMODE_BDETCT_RESET 0x08
+#define DOC_ASICMODE_RSTIN_RESET 0x10
+#define DOC_ASICMODE_RAM_WE 0x20
+
+/* good status values read after read/write/erase operations */
+#define DOCG4_PROGSTATUS_GOOD 0x51
+#define DOCG4_PROGSTATUS_GOOD_2 0xe0
+
+/*
+ * On read operations (page and oob-only), the first byte read from I/O reg is a
+ * status. On error, it reads 0x73; otherwise, it reads either 0x71 (first read
+ * after reset only) or 0x51, so bit 1 is presumed to be an error indicator.
+ */
+#define DOCG4_READ_ERROR 0x02 /* bit 1 indicates read error */
+
+/* anatomy of the device */
+#define DOCG4_CHIP_SIZE 0x8000000
+#define DOCG4_PAGE_SIZE 0x200
+#define DOCG4_PAGES_PER_BLOCK 0x200
+#define DOCG4_BLOCK_SIZE (DOCG4_PAGES_PER_BLOCK * DOCG4_PAGE_SIZE)
+#define DOCG4_NUMBLOCKS (DOCG4_CHIP_SIZE / DOCG4_BLOCK_SIZE)
+#define DOCG4_OOB_SIZE 0x10
+#define DOCG4_CHIP_SHIFT 27 /* log_2(DOCG4_CHIP_SIZE) */
+#define DOCG4_PAGE_SHIFT 9 /* log_2(DOCG4_PAGE_SIZE) */
+#define DOCG4_ERASE_SHIFT 18 /* log_2(DOCG4_BLOCK_SIZE) */
+
+/* all but the last byte is included in ecc calculation */
+#define DOCG4_BCH_SIZE (DOCG4_PAGE_SIZE + DOCG4_OOB_SIZE - 1)
+
+#define DOCG4_USERDATA_LEN 520 /* 512 byte page plus 8 oob avail to user */
+
+/* expected values from the ID registers */
+#define DOCG4_IDREG1_VALUE 0x0400
+#define DOCG4_IDREG2_VALUE 0xfbff
+
+/* primitive polynomial used to build the Galois field used by hw ecc gen */
+#define DOCG4_PRIMITIVE_POLY 0x4443
+
+#define DOCG4_M 14 /* Galois field is of order 2^14 */
+#define DOCG4_T 4 /* BCH alg corrects up to 4 bit errors */
+
+#define DOCG4_FACTORY_BBT_PAGE 16 /* page where read-only factory bbt lives */
+
+/*
+ * Oob bytes 0 - 6 are available to the user.
+ * Byte 7 is hamming ecc for first 7 bytes. Bytes 8 - 14 are hw-generated ecc.
+ * Byte 15 (the last) is used by the driver as a "page written" flag.
+ */
+static struct nand_ecclayout docg4_oobinfo = {
+ .eccbytes = 9,
+ .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
+ .oobavail = 7,
+ .oobfree = { {0, 7} }
+};
+
+/*
+ * The device has a nop register which M-Sys claims is for the purpose of
+ * inserting precise delays. But beware; at least some operations fail if the
+ * nop writes are replaced with a generic delay!
+ */
+static inline void write_nop(void __iomem *docptr)
+{
+ writew(0, docptr + DOC_NOP);
+}
+
+static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *nand = mtd->priv;
+ uint16_t *p = (uint16_t *) buf;
+ len >>= 1;
+
+ for (i = 0; i < len; i++)
+ p[i] = readw(nand->IO_ADDR_R);
+}
+
+static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *nand = mtd->priv;
+ uint16_t *p = (uint16_t *) buf;
+ len >>= 1;
+
+ for (i = 0; i < len; i++)
+ writew(p[i], nand->IO_ADDR_W);
+}
+
+static int poll_status(struct docg4_priv *doc)
+{
+ /*
+ * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL
+ * register. Operations known to take a long time (e.g., block erase)
+ * should sleep for a while before calling this.
+ */
+
+ uint16_t flash_status;
+ unsigned int timeo;
+ void __iomem *docptr = doc->virtadr;
+
+ dev_dbg(doc->dev, "%s...\n", __func__);
+
+ /* hardware quirk requires reading twice initially */
+ flash_status = readw(docptr + DOC_FLASHCONTROL);
+
+ timeo = 1000;
+ do {
+ cpu_relax();
+ flash_status = readb(docptr + DOC_FLASHCONTROL);
+ } while (!(flash_status & DOC_CTRL_FLASHREADY) && --timeo);
+
+
+ if (!timeo) {
+ dev_err(doc->dev, "%s: timed out!\n", __func__);
+ return NAND_STATUS_FAIL;
+ }
+
+ if (unlikely(timeo < 50))
+ dev_warn(doc->dev, "%s: nearly timed out; %d remaining\n",
+ __func__, timeo);
+
+ return 0;
+}
+
+
+static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand)
+{
+
+ struct docg4_priv *doc = nand->priv;
+ int status = NAND_STATUS_WP; /* inverse logic?? */
+ dev_dbg(doc->dev, "%s...\n", __func__);
+
+ /* report any previously unreported error */
+ if (doc->status) {
+ status |= doc->status;
+ doc->status = 0;
+ return status;
+ }
+
+ status |= poll_status(doc);
+ return status;
+}
+
+static void docg4_select_chip(struct mtd_info *mtd, int chip)
+{
+ /*
+ * Select among multiple cascaded chips ("floors"). Multiple floors are
+ * not yet supported, so the only valid non-negative value is 0.
+ */
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ void __iomem *docptr = doc->virtadr;
+
+ dev_dbg(doc->dev, "%s: chip %d\n", __func__, chip);
+
+ if (chip < 0)
+ return; /* deselected */
+
+ if (chip > 0)
+ dev_warn(doc->dev, "multiple floors currently unsupported\n");
+
+ writew(0, docptr + DOC_DEVICESELECT);
+}
+
+static void reset(struct mtd_info *mtd)
+{
+ /* full device reset */
+
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ void __iomem *docptr = doc->virtadr;
+
+ writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN,
+ docptr + DOC_ASICMODE);
+ writew(~(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN),
+ docptr + DOC_ASICMODECONFIRM);
+ write_nop(docptr);
+
+ writew(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN,
+ docptr + DOC_ASICMODE);
+ writew(~(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN),
+ docptr + DOC_ASICMODECONFIRM);
+
+ writew(DOC_ECCCONF1_ECC_ENABLE, docptr + DOC_ECCCONF1);
+
+ poll_status(doc);
+}
+
+static void read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf)
+{
+ /* read the 7 hw-generated ecc bytes */
+
+ int i;
+ for (i = 0; i < 7; i++) { /* hw quirk; read twice */
+ ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i));
+ ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i));
+ }
+}
+
+static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page)
+{
+ /*
+ * Called after a page read when hardware reports bitflips.
+ * Up to four bitflips can be corrected.
+ */
+
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ void __iomem *docptr = doc->virtadr;
+ int i, numerrs, errpos[4];
+ const uint8_t blank_read_hwecc[8] = {
+ 0xcf, 0x72, 0xfc, 0x1b, 0xa9, 0xc7, 0xb9, 0 };
+
+ read_hw_ecc(docptr, doc->ecc_buf); /* read 7 hw-generated ecc bytes */
+
+ /* check if read error is due to a blank page */
+ if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7))
+ return 0; /* yes */
+
+ /* skip additional check of "written flag" if ignore_badblocks */
+ if (ignore_badblocks == false) {
+
+ /*
+ * If the hw ecc bytes are not those of a blank page, there's
+ * still a chance that the page is blank, but was read with
+ * errors. Check the "written flag" in last oob byte, which
+ * is set to zero when a page is written. If more than half
+ * the bits are set, assume a blank page. Unfortunately, the
+ * bit flips(s) are not reported in stats.
+ */
+
+ if (doc->oob_buf[15]) {
+ int bit, numsetbits = 0;
+ unsigned long written_flag = doc->oob_buf[15];
+ for_each_set_bit(bit, &written_flag, 8)
+ numsetbits++;
+ if (numsetbits > 4) { /* assume blank */
+ dev_warn(doc->dev,
+ "error(s) in blank page "
+ "at offset %08x\n",
+ page * DOCG4_PAGE_SIZE);
+ return 0;
+ }
+ }
+ }
+
+ /*
+ * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch
+ * algorithm is used to decode this. However the hw operates on page
+ * data in a bit order that is the reverse of that of the bch alg,
+ * requiring that the bits be reversed on the result. Thanks to Ivan
+ * Djelic for his analysis!
+ */
+ for (i = 0; i < 7; i++)
+ doc->ecc_buf[i] = bitrev8(doc->ecc_buf[i]);
+
+ numerrs = decode_bch(doc->bch, NULL, DOCG4_USERDATA_LEN, NULL,
+ doc->ecc_buf, NULL, errpos);
+
+ if (numerrs == -EBADMSG) {
+ dev_warn(doc->dev, "uncorrectable errors at offset %08x\n",
+ page * DOCG4_PAGE_SIZE);
+ return -EBADMSG;
+ }
+
+ BUG_ON(numerrs < 0); /* -EINVAL, or anything other than -EBADMSG */
+
+ /* undo last step in BCH alg (modulo mirroring not needed) */
+ for (i = 0; i < numerrs; i++)
+ errpos[i] = (errpos[i] & ~7)|(7-(errpos[i] & 7));
+
+ /* fix the errors */
+ for (i = 0; i < numerrs; i++) {
+
+ /* ignore if error within oob ecc bytes */
+ if (errpos[i] > DOCG4_USERDATA_LEN * 8)
+ continue;
+
+ /* if error within oob area preceeding ecc bytes... */
+ if (errpos[i] > DOCG4_PAGE_SIZE * 8)
+ change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8,
+ (unsigned long *)doc->oob_buf);
+
+ else /* error in page data */
+ change_bit(errpos[i], (unsigned long *)buf);
+ }
+
+ dev_notice(doc->dev, "%d error(s) corrected at offset %08x\n",
+ numerrs, page * DOCG4_PAGE_SIZE);
+
+ return numerrs;
+}
+
+static uint8_t docg4_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+
+ dev_dbg(doc->dev, "%s\n", __func__);
+
+ if (doc->last_command.command == NAND_CMD_STATUS) {
+ int status;
+
+ /*
+ * Previous nand command was status request, so nand
+ * infrastructure code expects to read the status here. If an
+ * error occurred in a previous operation, report it.
+ */
+ doc->last_command.command = 0;
+
+ if (doc->status) {
+ status = doc->status;
+ doc->status = 0;
+ }
+
+ /* why is NAND_STATUS_WP inverse logic?? */
+ else
+ status = NAND_STATUS_WP | NAND_STATUS_READY;
+
+ return status;
+ }
+
+ dev_warn(doc->dev, "unexpectd call to read_byte()\n");
+
+ return 0;
+}
+
+static void write_addr(struct docg4_priv *doc, uint32_t docg4_addr)
+{
+ /* write the four address bytes packed in docg4_addr to the device */
+
+ void __iomem *docptr = doc->virtadr;
+ writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
+ docg4_addr >>= 8;
+ writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
+ docg4_addr >>= 8;
+ writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
+ docg4_addr >>= 8;
+ writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS);
+}
+
+static int read_progstatus(struct docg4_priv *doc)
+{
+ /*
+ * This apparently checks the status of programming. Done after an
+ * erasure, and after page data is written. On error, the status is
+ * saved, to be later retrieved by the nand infrastructure code.
+ */
+ void __iomem *docptr = doc->virtadr;
+
+ /* status is read from the I/O reg */
+ uint16_t status1 = readw(docptr + DOC_IOSPACE_DATA);
+ uint16_t status2 = readw(docptr + DOC_IOSPACE_DATA);
+ uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG);
+
+ dev_dbg(doc->dev, "docg4: %s: %02x %02x %02x\n",
+ __func__, status1, status2, status3);
+
+ if (status1 != DOCG4_PROGSTATUS_GOOD
+ || status2 != DOCG4_PROGSTATUS_GOOD_2
+ || status3 != DOCG4_PROGSTATUS_GOOD_2) {
+ doc->status = NAND_STATUS_FAIL;
+ dev_warn(doc->dev, "read_progstatus failed: "
+ "%02x, %02x, %02x\n", status1, status2, status3);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int pageprog(struct mtd_info *mtd)
+{
+ /*
+ * Final step in writing a page. Writes the contents of its
+ * internal buffer out to the flash array, or some such.
+ */
+
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ void __iomem *docptr = doc->virtadr;
+ int retval = 0;
+
+ dev_dbg(doc->dev, "docg4: %s\n", __func__);
+
+ writew(DOCG4_SEQ_PAGEPROG, docptr + DOC_FLASHSEQUENCE);
+ writew(DOC_CMD_PROG_CYCLE2, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ /* Just busy-wait; usleep_range() slows things down noticeably. */
+ poll_status(doc);
+
+ writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE);
+ writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND);
+ writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ retval = read_progstatus(doc);
+ writew(0, docptr + DOC_DATAEND);
+ write_nop(docptr);
+ poll_status(doc);
+ write_nop(docptr);
+
+ return retval;
+}
+
+static void sequence_reset(struct mtd_info *mtd)
+{
+ /* common starting sequence for all operations */
+
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ void __iomem *docptr = doc->virtadr;
+
+ writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL);
+ writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE);
+ writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+ write_nop(docptr);
+ poll_status(doc);
+ write_nop(docptr);
+}
+
+static void read_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr)
+{
+ /* first step in reading a page */
+
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ void __iomem *docptr = doc->virtadr;
+
+ dev_dbg(doc->dev,
+ "docg4: %s: g4 page %08x\n", __func__, docg4_addr);
+
+ sequence_reset(mtd);
+
+ writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE);
+ writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+
+ write_addr(doc, docg4_addr);
+
+ write_nop(docptr);
+ writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ poll_status(doc);
+}
+
+static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr)
+{
+ /* first step in writing a page */
+
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ void __iomem *docptr = doc->virtadr;
+
+ dev_dbg(doc->dev,
+ "docg4: %s: g4 addr: %x\n", __func__, docg4_addr);
+ sequence_reset(mtd);
+ writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE);
+ writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+ write_addr(doc, docg4_addr);
+ write_nop(docptr);
+ write_nop(docptr);
+ poll_status(doc);
+}
+
+static uint32_t mtd_to_docg4_address(int page, int column)
+{
+ /*
+ * Convert mtd address to format used by the device, 32 bit packed.
+ *
+ * Some notes on G4 addressing... The M-Sys documentation on this device
+ * claims that pages are 2K in length, and indeed, the format of the
+ * address used by the device reflects that. But within each page are
+ * four 512 byte "sub-pages", each with its own oob data that is
+ * read/written immediately after the 512 bytes of page data. This oob
+ * data contains the ecc bytes for the preceeding 512 bytes.
+ *
+ * Rather than tell the mtd nand infrastructure that page size is 2k,
+ * with four sub-pages each, we engage in a little subterfuge and tell
+ * the infrastructure code that pages are 512 bytes in size. This is
+ * done because during the course of reverse-engineering the device, I
+ * never observed an instance where an entire 2K "page" was read or
+ * written as a unit. Each "sub-page" is always addressed individually,
+ * its data read/written, and ecc handled before the next "sub-page" is
+ * addressed.
+ *
+ * This requires us to convert addresses passed by the mtd nand
+ * infrastructure code to those used by the device.
+ *
+ * The address that is written to the device consists of four bytes: the
+ * first two are the 2k page number, and the second is the index into
+ * the page. The index is in terms of 16-bit half-words and includes
+ * the preceeding oob data, so e.g., the index into the second
+ * "sub-page" is 0x108, and the full device address of the start of mtd
+ * page 0x201 is 0x00800108.
+ */
+ int g4_page = page / 4; /* device's 2K page */
+ int g4_index = (page % 4) * 0x108 + column/2; /* offset into page */
+ return (g4_page << 16) | g4_index; /* pack */
+}
+
+static void docg4_command(struct mtd_info *mtd, unsigned command, int column,
+ int page_addr)
+{
+ /* handle standard nand commands */
+
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ uint32_t g4_addr = mtd_to_docg4_address(page_addr, column);
+
+ dev_dbg(doc->dev, "%s %x, page_addr=%x, column=%x\n",
+ __func__, command, page_addr, column);
+
+ /*
+ * Save the command and its arguments. This enables emulation of
+ * standard flash devices, and also some optimizations.
+ */
+ doc->last_command.command = command;
+ doc->last_command.column = column;
+ doc->last_command.page = page_addr;
+
+ switch (command) {
+
+ case NAND_CMD_RESET:
+ reset(mtd);
+ break;
+
+ case NAND_CMD_READ0:
+ read_page_prologue(mtd, g4_addr);
+ break;
+
+ case NAND_CMD_STATUS:
+ /* next call to read_byte() will expect a status */
+ break;
+
+ case NAND_CMD_SEQIN:
+ write_page_prologue(mtd, g4_addr);
+
+ /* hack for deferred write of oob bytes */
+ if (doc->oob_page == page_addr)
+ memcpy(nand->oob_poi, doc->oob_buf, 16);
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ pageprog(mtd);
+ break;
+
+ /* we don't expect these, based on review of nand_base.c */
+ case NAND_CMD_READOOB:
+ case NAND_CMD_READID:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ dev_warn(doc->dev, "docg4_command: "
+ "unexpected nand command 0x%x\n", command);
+ break;
+
+ }
+}
+
+static int read_page(struct mtd_info *mtd, struct nand_chip *nand,
+ uint8_t *buf, int page, bool use_ecc)
+{
+ struct docg4_priv *doc = nand->priv;
+ void __iomem *docptr = doc->virtadr;
+ uint16_t status, edc_err, *buf16;
+
+ dev_dbg(doc->dev, "%s: page %08x\n", __func__, page);
+
+ writew(DOC_ECCCONF0_READ_MODE |
+ DOC_ECCCONF0_ECC_ENABLE |
+ DOC_ECCCONF0_UNKNOWN |
+ DOCG4_BCH_SIZE,
+ docptr + DOC_ECCCONF0);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ /* the 1st byte from the I/O reg is a status; the rest is page data */
+ status = readw(docptr + DOC_IOSPACE_DATA);
+ if (status & DOCG4_READ_ERROR) {
+ dev_err(doc->dev,
+ "docg4_read_page: bad status: 0x%02x\n", status);
+ writew(0, docptr + DOC_DATAEND);
+ return -EIO;
+ }
+
+ dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status);
+
+ docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */
+
+ /*
+ * Diskonchips read oob immediately after a page read. Mtd
+ * infrastructure issues a separate command for reading oob after the
+ * page is read. So we save the oob bytes in a local buffer and just
+ * copy it if the next command reads oob from the same page.
+ */
+
+ /* first 14 oob bytes read from I/O reg */
+ docg4_read_buf(mtd, doc->oob_buf, 14);
+
+ /* last 2 read from another reg */
+ buf16 = (uint16_t *)(doc->oob_buf + 14);
+ *buf16 = readw(docptr + DOCG4_MYSTERY_REG);
+
+ write_nop(docptr);
+
+ if (likely(use_ecc == true)) {
+
+ /* read the register that tells us if bitflip(s) detected */
+ edc_err = readw(docptr + DOC_ECCCONF1);
+ edc_err = readw(docptr + DOC_ECCCONF1);
+ dev_dbg(doc->dev, "%s: edc_err = 0x%02x\n", __func__, edc_err);
+
+ /* If bitflips are reported, attempt to correct with ecc */
+ if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) {
+ int bits_corrected = correct_data(mtd, buf, page);
+ if (bits_corrected == -EBADMSG)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += bits_corrected;
+ }
+ }
+
+ writew(0, docptr + DOC_DATAEND);
+ return 0;
+}
+
+
+static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
+ uint8_t *buf, int page)
+{
+ return read_page(mtd, nand, buf, page, false);
+}
+
+static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand,
+ uint8_t *buf, int page)
+{
+ return read_page(mtd, nand, buf, page, true);
+}
+
+static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
+ int page, int sndcmd)
+{
+ struct docg4_priv *doc = nand->priv;
+ void __iomem *docptr = doc->virtadr;
+ uint16_t status;
+
+ dev_dbg(doc->dev, "%s: page %x\n", __func__, page);
+
+ /*
+ * Oob bytes are read as part of a normal page read. If the previous
+ * nand command was a read of the page whose oob is now being read, just
+ * copy the oob bytes that we saved in a local buffer and avoid a
+ * separate oob read.
+ */
+ if (doc->last_command.command == NAND_CMD_READ0 &&
+ doc->last_command.page == page) {
+ memcpy(nand->oob_poi, doc->oob_buf, 16);
+ return 0;
+ }
+
+ /*
+ * Separate read of oob data only.
+ */
+ docg4_command(mtd, NAND_CMD_READ0, nand->ecc.size, page);
+
+ writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ /* the 1st byte from the I/O reg is a status; the rest is oob data */
+ status = readw(docptr + DOC_IOSPACE_DATA);
+ if (status & DOCG4_READ_ERROR) {
+ dev_warn(doc->dev,
+ "docg4_read_oob failed: status = 0x%02x\n", status);
+ return -EIO;
+ }
+
+ dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status);
+
+ docg4_read_buf(mtd, nand->oob_poi, 16);
+
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ writew(0, docptr + DOC_DATAEND);
+ write_nop(docptr);
+
+ return 0;
+}
+
+static void docg4_erase_block(struct mtd_info *mtd, int page)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ void __iomem *docptr = doc->virtadr;
+ uint16_t g4_page;
+
+ dev_dbg(doc->dev, "%s: page %04x\n", __func__, page);
+
+ sequence_reset(mtd);
+
+ writew(DOCG4_SEQ_BLOCKERASE, docptr + DOC_FLASHSEQUENCE);
+ writew(DOC_CMD_PROG_BLOCK_ADDR, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+
+ /* only 2 bytes of address are written to specify erase block */
+ g4_page = (uint16_t)(page / 4); /* to g4's 2k page addressing */
+ writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS);
+ g4_page >>= 8;
+ writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS);
+ write_nop(docptr);
+
+ /* start the erasure */
+ writew(DOC_CMD_ERASECYCLE2, docptr + DOC_FLASHCOMMAND);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ usleep_range(500, 1000); /* erasure is long; take a snooze */
+ poll_status(doc);
+ writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE);
+ writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND);
+ writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+ write_nop(docptr);
+
+ read_progstatus(doc);
+
+ writew(0, docptr + DOC_DATAEND);
+ write_nop(docptr);
+ poll_status(doc);
+ write_nop(docptr);
+}
+
+static void write_page(struct mtd_info *mtd, struct nand_chip *nand,
+ const uint8_t *buf, bool use_ecc)
+{
+ struct docg4_priv *doc = nand->priv;
+ void __iomem *docptr = doc->virtadr;
+ uint8_t ecc_buf[8];
+
+ dev_dbg(doc->dev, "%s...\n", __func__);
+
+ writew(DOC_ECCCONF0_ECC_ENABLE |
+ DOC_ECCCONF0_UNKNOWN |
+ DOCG4_BCH_SIZE,
+ docptr + DOC_ECCCONF0);
+ write_nop(docptr);
+
+ /* write the page data */
+ docg4_write_buf16(mtd, buf, DOCG4_PAGE_SIZE);
+
+ /* oob bytes 0 through 5 are written to I/O reg */
+ docg4_write_buf16(mtd, nand->oob_poi, 6);
+
+ /* oob byte 6 written to a separate reg */
+ writew(nand->oob_poi[6], docptr + DOCG4_OOB_6_7);
+
+ write_nop(docptr);
+ write_nop(docptr);
+
+ /* write hw-generated ecc bytes to oob */
+ if (likely(use_ecc == true)) {
+ /* oob byte 7 is hamming code */
+ uint8_t hamming = readb(docptr + DOC_HAMMINGPARITY);
+ hamming = readb(docptr + DOC_HAMMINGPARITY); /* 2nd read */
+ writew(hamming, docptr + DOCG4_OOB_6_7);
+ write_nop(docptr);
+
+ /* read the 7 bch bytes from ecc regs */
+ read_hw_ecc(docptr, ecc_buf);
+ ecc_buf[7] = 0; /* clear the "page written" flag */
+ }
+
+ /* write user-supplied bytes to oob */
+ else {
+ writew(nand->oob_poi[7], docptr + DOCG4_OOB_6_7);
+ write_nop(docptr);
+ memcpy(ecc_buf, &nand->oob_poi[8], 8);
+ }
+
+ docg4_write_buf16(mtd, ecc_buf, 8);
+ write_nop(docptr);
+ write_nop(docptr);
+ writew(0, docptr + DOC_DATAEND);
+ write_nop(docptr);
+}
+
+static void docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
+ const uint8_t *buf)
+{
+ return write_page(mtd, nand, buf, false);
+}
+
+static void docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand,
+ const uint8_t *buf)
+{
+ return write_page(mtd, nand, buf, true);
+}
+
+static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand,
+ int page)
+{
+ /*
+ * Writing oob-only is not really supported, because MLC nand must write
+ * oob bytes at the same time as page data. Nonetheless, we save the
+ * oob buffer contents here, and then write it along with the page data
+ * if the same page is subsequently written. This allows user space
+ * utilities that write the oob data prior to the page data to work
+ * (e.g., nandwrite). The disdvantage is that, if the intention was to
+ * write oob only, the operation is quietly ignored. Also, oob can get
+ * corrupted if two concurrent processes are running nandwrite.
+ */
+
+ /* note that bytes 7..14 are hw generated hamming/ecc and overwritten */
+ struct docg4_priv *doc = nand->priv;
+ doc->oob_page = page;
+ memcpy(doc->oob_buf, nand->oob_poi, 16);
+ return 0;
+}
+
+static int __init read_factory_bbt(struct mtd_info *mtd)
+{
+ /*
+ * The device contains a read-only factory bad block table. Read it and
+ * update the memory-based bbt accordingly.
+ */
+
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0);
+ uint8_t *buf;
+ int i, block, status;
+
+ buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ read_page_prologue(mtd, g4_addr);
+ status = docg4_read_page(mtd, nand, buf, DOCG4_FACTORY_BBT_PAGE);
+ if (status)
+ goto exit;
+
+ /*
+ * If no memory-based bbt was created, exit. This will happen if module
+ * parameter ignore_badblocks is set. Then why even call this function?
+ * For an unknown reason, block erase always fails if it's the first
+ * operation after device power-up. The above read ensures it never is.
+ * Ugly, I know.
+ */
+ if (nand->bbt == NULL) /* no memory-based bbt */
+ goto exit;
+
+ /*
+ * Parse factory bbt and update memory-based bbt. Factory bbt format is
+ * simple: one bit per block, block numbers increase left to right (msb
+ * to lsb). Bit clear means bad block.
+ */
+ for (i = block = 0; block < DOCG4_NUMBLOCKS; block += 8, i++) {
+ int bitnum;
+ unsigned long bits = ~buf[i];
+ for_each_set_bit(bitnum, &bits, 8) {
+ int badblock = block + 7 - bitnum;
+ nand->bbt[badblock / 4] |=
+ 0x03 << ((badblock % 4) * 2);
+ mtd->ecc_stats.badblocks++;
+ dev_notice(doc->dev, "factory-marked bad block: %d\n",
+ badblock);
+ }
+ }
+ exit:
+ kfree(buf);
+ return status;
+}
+
+static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ /*
+ * Mark a block as bad. Bad blocks are marked in the oob area of the
+ * first page of the block. The default scan_bbt() in the nand
+ * infrastructure code works fine for building the memory-based bbt
+ * during initialization, as does the nand infrastructure function that
+ * checks if a block is bad by reading the bbt. This function replaces
+ * the nand default because writes to oob-only are not supported.
+ */
+
+ int ret, i;
+ uint8_t *buf;
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ struct nand_bbt_descr *bbtd = nand->badblock_pattern;
+ int block = (int)(ofs >> nand->bbt_erase_shift);
+ int page = (int)(ofs >> nand->page_shift);
+ uint32_t g4_addr = mtd_to_docg4_address(page, 0);
+
+ dev_dbg(doc->dev, "%s: %08llx\n", __func__, ofs);
+
+ if (unlikely(ofs & (DOCG4_BLOCK_SIZE - 1)))
+ dev_warn(doc->dev, "%s: ofs %llx not start of block!\n",
+ __func__, ofs);
+
+ /* allocate blank buffer for page data */
+ buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* update bbt in memory */
+ nand->bbt[block / 4] |= 0x01 << ((block & 0x03) * 2);
+
+ /* write bit-wise negation of pattern to oob buffer */
+ memset(nand->oob_poi, 0xff, mtd->oobsize);
+ for (i = 0; i < bbtd->len; i++)
+ nand->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i];
+
+ /* write first page of block */
+ write_page_prologue(mtd, g4_addr);
+ docg4_write_page(mtd, nand, buf);
+ ret = pageprog(mtd);
+ if (!ret)
+ mtd->ecc_stats.badblocks++;
+
+ kfree(buf);
+
+ return ret;
+}
+
+static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+ /* only called when module_param ignore_badblocks is set */
+ return 0;
+}
+
+static int docg4_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ /*
+ * Put the device into "deep power-down" mode. Note that CE# must be
+ * deasserted for this to take effect. The xscale, e.g., can be
+ * configured to float this signal when the processor enters power-down,
+ * and a suitable pull-up ensures its deassertion.
+ */
+
+ int i;
+ uint8_t pwr_down;
+ struct docg4_priv *doc = platform_get_drvdata(pdev);
+ void __iomem *docptr = doc->virtadr;
+
+ dev_dbg(doc->dev, "%s...\n", __func__);
+
+ /* poll the register that tells us we're ready to go to sleep */
+ for (i = 0; i < 10; i++) {
+ pwr_down = readb(docptr + DOC_POWERMODE);
+ if (pwr_down & DOC_POWERDOWN_READY)
+ break;
+ usleep_range(1000, 4000);
+ }
+
+ if (pwr_down & DOC_POWERDOWN_READY) {
+ dev_err(doc->dev, "suspend failed; "
+ "timeout polling DOC_POWERDOWN_READY\n");
+ return -EIO;
+ }
+
+ writew(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN,
+ docptr + DOC_ASICMODE);
+ writew(~(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN),
+ docptr + DOC_ASICMODECONFIRM);
+
+ write_nop(docptr);
+
+ return 0;
+}
+
+static int docg4_resume(struct platform_device *pdev)
+{
+
+ /*
+ * Exit power-down. Twelve consecutive reads of the address below
+ * accomplishes this, assuming CE# has been asserted.
+ */
+
+ struct docg4_priv *doc = platform_get_drvdata(pdev);
+ void __iomem *docptr = doc->virtadr;
+ int i;
+
+ dev_dbg(doc->dev, "%s...\n", __func__);
+
+ for (i = 0; i < 12; i++)
+ readb(docptr + 0x1fff);
+
+ return 0;
+}
+
+static void __init init_mtd_structs(struct mtd_info *mtd)
+{
+ /* initialize mtd and nand data structures */
+
+ /*
+ * Note that some of the following initializations are not usually
+ * required within a nand driver because they are performed by the nand
+ * infrastructure code as part of nand_scan(). In this case they need
+ * to be initialized here because we skip call to nand_scan_ident() (the
+ * first half of nand_scan()). The call to nand_scan_ident() is skipped
+ * because for this device the chip id is not read in the manner of a
+ * standard nand device. Unfortunately, nand_scan_ident() does other
+ * things as well, such as call nand_set_defaults().
+ */
+
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+
+ mtd->size = DOCG4_CHIP_SIZE;
+ mtd->name = "Msys_Diskonchip_G4";
+ mtd->writesize = DOCG4_PAGE_SIZE;
+ mtd->erasesize = DOCG4_BLOCK_SIZE;
+ mtd->oobsize = DOCG4_OOB_SIZE;
+ nand->chipsize = DOCG4_CHIP_SIZE;
+ nand->chip_shift = DOCG4_CHIP_SHIFT;
+ nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT;
+ nand->chip_delay = 20;
+ nand->page_shift = DOCG4_PAGE_SHIFT;
+ nand->pagemask = 0x3ffff;
+ nand->badblockpos = NAND_LARGE_BADBLOCK_POS;
+ nand->badblockbits = 8;
+ nand->ecc.layout = &docg4_oobinfo;
+ nand->ecc.mode = NAND_ECC_HW_SYNDROME;
+ nand->ecc.size = DOCG4_PAGE_SIZE;
+ nand->ecc.prepad = 8;
+ nand->ecc.bytes = 8;
+ nand->ecc.strength = DOCG4_T;
+ nand->options =
+ NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE | NAND_NO_AUTOINCR;
+ nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA;
+ nand->controller = &nand->hwcontrol;
+ spin_lock_init(&nand->controller->lock);
+ init_waitqueue_head(&nand->controller->wq);
+
+ /* methods */
+ nand->cmdfunc = docg4_command;
+ nand->waitfunc = docg4_wait;
+ nand->select_chip = docg4_select_chip;
+ nand->read_byte = docg4_read_byte;
+ nand->block_markbad = docg4_block_markbad;
+ nand->read_buf = docg4_read_buf;
+ nand->write_buf = docg4_write_buf16;
+ nand->scan_bbt = nand_default_bbt;
+ nand->erase_cmd = docg4_erase_block;
+ nand->ecc.read_page = docg4_read_page;
+ nand->ecc.write_page = docg4_write_page;
+ nand->ecc.read_page_raw = docg4_read_page_raw;
+ nand->ecc.write_page_raw = docg4_write_page_raw;
+ nand->ecc.read_oob = docg4_read_oob;
+ nand->ecc.write_oob = docg4_write_oob;
+
+ /*
+ * The way the nand infrastructure code is written, a memory-based bbt
+ * is not created if NAND_SKIP_BBTSCAN is set. With no memory bbt,
+ * nand->block_bad() is used. So when ignoring bad blocks, we skip the
+ * scan and define a dummy block_bad() which always returns 0.
+ */
+ if (ignore_badblocks) {
+ nand->options |= NAND_SKIP_BBTSCAN;
+ nand->block_bad = docg4_block_neverbad;
+ }
+
+}
+
+static int __init read_id_reg(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ void __iomem *docptr = doc->virtadr;
+ uint16_t id1, id2;
+
+ /* check for presence of g4 chip by reading id registers */
+ id1 = readw(docptr + DOC_CHIPID);
+ id1 = readw(docptr + DOCG4_MYSTERY_REG);
+ id2 = readw(docptr + DOC_CHIPID_INV);
+ id2 = readw(docptr + DOCG4_MYSTERY_REG);
+
+ if (id1 == DOCG4_IDREG1_VALUE && id2 == DOCG4_IDREG2_VALUE) {
+ dev_info(doc->dev,
+ "NAND device: 128MiB Diskonchip G4 detected\n");
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL };
+
+static int __init probe_docg4(struct platform_device *pdev)
+{
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ void __iomem *virtadr;
+ struct docg4_priv *doc;
+ int len, retval;
+ struct resource *r;
+ struct device *dev = &pdev->dev;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ dev_err(dev, "no io memory resource defined!\n");
+ return -ENODEV;
+ }
+
+ virtadr = ioremap(r->start, resource_size(r));
+ if (!virtadr) {
+ dev_err(dev, "Diskonchip ioremap failed: %pR\n", r);
+ return -EIO;
+ }
+
+ len = sizeof(struct mtd_info) + sizeof(struct nand_chip) +
+ sizeof(struct docg4_priv);
+ mtd = kzalloc(len, GFP_KERNEL);
+ if (mtd == NULL) {
+ retval = -ENOMEM;
+ goto fail;
+ }
+ nand = (struct nand_chip *) (mtd + 1);
+ doc = (struct docg4_priv *) (nand + 1);
+ mtd->priv = nand;
+ nand->priv = doc;
+ mtd->owner = THIS_MODULE;
+ doc->virtadr = virtadr;
+ doc->dev = dev;
+
+ init_mtd_structs(mtd);
+
+ /* initialize kernel bch algorithm */
+ doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY);
+ if (doc->bch == NULL) {
+ retval = -EINVAL;
+ goto fail;
+ }
+
+ platform_set_drvdata(pdev, doc);
+
+ reset(mtd);
+ retval = read_id_reg(mtd);
+ if (retval == -ENODEV) {
+ dev_warn(dev, "No diskonchip G4 device found.\n");
+ goto fail;
+ }
+
+ retval = nand_scan_tail(mtd);
+ if (retval)
+ goto fail;
+
+ retval = read_factory_bbt(mtd);
+ if (retval)
+ goto fail;
+
+ retval = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
+ if (retval)
+ goto fail;
+
+ doc->mtd = mtd;
+ return 0;
+
+ fail:
+ iounmap(virtadr);
+ if (mtd) {
+ /* re-declarations avoid compiler warning */
+ struct nand_chip *nand = mtd->priv;
+ struct docg4_priv *doc = nand->priv;
+ nand_release(mtd); /* deletes partitions and mtd devices */
+ platform_set_drvdata(pdev, NULL);
+ free_bch(doc->bch);
+ kfree(mtd);
+ }
+
+ return retval;
+}
+
+static int __exit cleanup_docg4(struct platform_device *pdev)
+{
+ struct docg4_priv *doc = platform_get_drvdata(pdev);
+ nand_release(doc->mtd);
+ platform_set_drvdata(pdev, NULL);
+ free_bch(doc->bch);
+ kfree(doc->mtd);
+ iounmap(doc->virtadr);
+ return 0;
+}
+
+static struct platform_driver docg4_driver = {
+ .driver = {
+ .name = "docg4",
+ .owner = THIS_MODULE,
+ },
+ .suspend = docg4_suspend,
+ .resume = docg4_resume,
+ .remove = __exit_p(cleanup_docg4),
+};
+
+static int __init docg4_init(void)
+{
+ return platform_driver_probe(&docg4_driver, probe_docg4);
+}
+
+static void __exit docg4_exit(void)
+{
+ platform_driver_unregister(&docg4_driver);
+}
+
+module_init(docg4_init);
+module_exit(docg4_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Dunn");
+MODULE_DESCRIPTION("M-Systems DiskOnChip G4 device driver");
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index 7195ee6..80b5264 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -813,6 +813,12 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
&fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0;
chip->ecc.size = 512;
chip->ecc.bytes = 3;
+ chip->ecc.strength = 1;
+ /*
+ * FIXME: can hardware ecc correct 4 bitflips if page size is
+ * 2k? Then does hardware report number of corrections for this
+ * case? If so, ecc_stats reporting needs to be fixed as well.
+ */
} else {
/* otherwise fall back to default software ECC */
chip->ecc.mode = NAND_ECC_SOFT;
diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c
index e53b760..1b8330e 100644
--- a/drivers/mtd/nand/fsmc_nand.c
+++ b/drivers/mtd/nand/fsmc_nand.c
@@ -17,6 +17,10 @@
*/
#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -27,6 +31,7 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -34,7 +39,7 @@
#include <linux/amba/bus.h>
#include <mtd/mtd-abi.h>
-static struct nand_ecclayout fsmc_ecc1_layout = {
+static struct nand_ecclayout fsmc_ecc1_128_layout = {
.eccbytes = 24,
.eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52,
66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116},
@@ -50,7 +55,127 @@ static struct nand_ecclayout fsmc_ecc1_layout = {
}
};
-static struct nand_ecclayout fsmc_ecc4_lp_layout = {
+static struct nand_ecclayout fsmc_ecc1_64_layout = {
+ .eccbytes = 12,
+ .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52},
+ .oobfree = {
+ {.offset = 8, .length = 8},
+ {.offset = 24, .length = 8},
+ {.offset = 40, .length = 8},
+ {.offset = 56, .length = 8},
+ }
+};
+
+static struct nand_ecclayout fsmc_ecc1_16_layout = {
+ .eccbytes = 3,
+ .eccpos = {2, 3, 4},
+ .oobfree = {
+ {.offset = 8, .length = 8},
+ }
+};
+
+/*
+ * ECC4 layout for NAND of pagesize 8192 bytes & OOBsize 256 bytes. 13*16 bytes
+ * of OB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 46
+ * bytes are free for use.
+ */
+static struct nand_ecclayout fsmc_ecc4_256_layout = {
+ .eccbytes = 208,
+ .eccpos = { 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14,
+ 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30,
+ 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46,
+ 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, 62,
+ 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78,
+ 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94,
+ 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110,
+ 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126,
+ 130, 131, 132, 133, 134, 135, 136,
+ 137, 138, 139, 140, 141, 142,
+ 146, 147, 148, 149, 150, 151, 152,
+ 153, 154, 155, 156, 157, 158,
+ 162, 163, 164, 165, 166, 167, 168,
+ 169, 170, 171, 172, 173, 174,
+ 178, 179, 180, 181, 182, 183, 184,
+ 185, 186, 187, 188, 189, 190,
+ 194, 195, 196, 197, 198, 199, 200,
+ 201, 202, 203, 204, 205, 206,
+ 210, 211, 212, 213, 214, 215, 216,
+ 217, 218, 219, 220, 221, 222,
+ 226, 227, 228, 229, 230, 231, 232,
+ 233, 234, 235, 236, 237, 238,
+ 242, 243, 244, 245, 246, 247, 248,
+ 249, 250, 251, 252, 253, 254
+ },
+ .oobfree = {
+ {.offset = 15, .length = 3},
+ {.offset = 31, .length = 3},
+ {.offset = 47, .length = 3},
+ {.offset = 63, .length = 3},
+ {.offset = 79, .length = 3},
+ {.offset = 95, .length = 3},
+ {.offset = 111, .length = 3},
+ {.offset = 127, .length = 3},
+ {.offset = 143, .length = 3},
+ {.offset = 159, .length = 3},
+ {.offset = 175, .length = 3},
+ {.offset = 191, .length = 3},
+ {.offset = 207, .length = 3},
+ {.offset = 223, .length = 3},
+ {.offset = 239, .length = 3},
+ {.offset = 255, .length = 1}
+ }
+};
+
+/*
+ * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes
+ * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118
+ * bytes are free for use.
+ */
+static struct nand_ecclayout fsmc_ecc4_224_layout = {
+ .eccbytes = 104,
+ .eccpos = { 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14,
+ 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30,
+ 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46,
+ 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, 62,
+ 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78,
+ 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94,
+ 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110,
+ 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126
+ },
+ .oobfree = {
+ {.offset = 15, .length = 3},
+ {.offset = 31, .length = 3},
+ {.offset = 47, .length = 3},
+ {.offset = 63, .length = 3},
+ {.offset = 79, .length = 3},
+ {.offset = 95, .length = 3},
+ {.offset = 111, .length = 3},
+ {.offset = 127, .length = 97}
+ }
+};
+
+/*
+ * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 128 bytes. 13*8 bytes
+ * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 22
+ * bytes are free for use.
+ */
+static struct nand_ecclayout fsmc_ecc4_128_layout = {
.eccbytes = 104,
.eccpos = { 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14,
@@ -82,6 +207,45 @@ static struct nand_ecclayout fsmc_ecc4_lp_layout = {
};
/*
+ * ECC4 layout for NAND of pagesize 2048 bytes & OOBsize 64 bytes. 13*4 bytes of
+ * OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 10
+ * bytes are free for use.
+ */
+static struct nand_ecclayout fsmc_ecc4_64_layout = {
+ .eccbytes = 52,
+ .eccpos = { 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14,
+ 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30,
+ 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46,
+ 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, 62,
+ },
+ .oobfree = {
+ {.offset = 15, .length = 3},
+ {.offset = 31, .length = 3},
+ {.offset = 47, .length = 3},
+ {.offset = 63, .length = 1},
+ }
+};
+
+/*
+ * ECC4 layout for NAND of pagesize 512 bytes & OOBsize 16 bytes. 13 bytes of
+ * OOB size is reserved for ECC, Byte no. 4 & 5 reserved for bad block and One
+ * byte is free for use.
+ */
+static struct nand_ecclayout fsmc_ecc4_16_layout = {
+ .eccbytes = 13,
+ .eccpos = { 0, 1, 2, 3, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14
+ },
+ .oobfree = {
+ {.offset = 15, .length = 1},
+ }
+};
+
+/*
* ECC placement definitions in oobfree type format.
* There are 13 bytes of ecc for every 512 byte block and it has to be read
* consecutively and immediately after the 512 byte data block for hardware to
@@ -103,16 +267,6 @@ static struct fsmc_eccplace fsmc_ecc4_lp_place = {
}
};
-static struct nand_ecclayout fsmc_ecc4_sp_layout = {
- .eccbytes = 13,
- .eccpos = { 0, 1, 2, 3, 6, 7, 8,
- 9, 10, 11, 12, 13, 14
- },
- .oobfree = {
- {.offset = 15, .length = 1},
- }
-};
-
static struct fsmc_eccplace fsmc_ecc4_sp_place = {
.eccplace = {
{.offset = 0, .length = 4},
@@ -120,75 +274,24 @@ static struct fsmc_eccplace fsmc_ecc4_sp_place = {
}
};
-/*
- * Default partition tables to be used if the partition information not
- * provided through platform data.
- *
- * Default partition layout for small page(= 512 bytes) devices
- * Size for "Root file system" is updated in driver based on actual device size
- */
-static struct mtd_partition partition_info_16KB_blk[] = {
- {
- .name = "X-loader",
- .offset = 0,
- .size = 4*0x4000,
- },
- {
- .name = "U-Boot",
- .offset = 0x10000,
- .size = 20*0x4000,
- },
- {
- .name = "Kernel",
- .offset = 0x60000,
- .size = 256*0x4000,
- },
- {
- .name = "Root File System",
- .offset = 0x460000,
- .size = MTDPART_SIZ_FULL,
- },
-};
-
-/*
- * Default partition layout for large page(> 512 bytes) devices
- * Size for "Root file system" is updated in driver based on actual device size
- */
-static struct mtd_partition partition_info_128KB_blk[] = {
- {
- .name = "X-loader",
- .offset = 0,
- .size = 4*0x20000,
- },
- {
- .name = "U-Boot",
- .offset = 0x80000,
- .size = 12*0x20000,
- },
- {
- .name = "Kernel",
- .offset = 0x200000,
- .size = 48*0x20000,
- },
- {
- .name = "Root File System",
- .offset = 0x800000,
- .size = MTDPART_SIZ_FULL,
- },
-};
-
-
/**
* struct fsmc_nand_data - structure for FSMC NAND device state
*
* @pid: Part ID on the AMBA PrimeCell format
* @mtd: MTD info for a NAND flash.
* @nand: Chip related info for a NAND flash.
+ * @partitions: Partition info for a NAND Flash.
+ * @nr_partitions: Total number of partition of a NAND flash.
*
* @ecc_place: ECC placing locations in oobfree type format.
* @bank: Bank number for probed device.
* @clk: Clock structure for FSMC.
*
+ * @read_dma_chan: DMA channel for read access
+ * @write_dma_chan: DMA channel for write access to NAND
+ * @dma_access_complete: Completion structure
+ *
+ * @data_pa: NAND Physical port for Data.
* @data_va: NAND port for Data.
* @cmd_va: NAND port for Command.
* @addr_va: NAND port for Address.
@@ -198,16 +301,23 @@ struct fsmc_nand_data {
u32 pid;
struct mtd_info mtd;
struct nand_chip nand;
+ struct mtd_partition *partitions;
+ unsigned int nr_partitions;
struct fsmc_eccplace *ecc_place;
unsigned int bank;
+ struct device *dev;
+ enum access_mode mode;
struct clk *clk;
- struct resource *resregs;
- struct resource *rescmd;
- struct resource *resaddr;
- struct resource *resdata;
+ /* DMA related objects */
+ struct dma_chan *read_dma_chan;
+ struct dma_chan *write_dma_chan;
+ struct completion dma_access_complete;
+
+ struct fsmc_nand_timings *dev_timings;
+ dma_addr_t data_pa;
void __iomem *data_va;
void __iomem *cmd_va;
void __iomem *addr_va;
@@ -251,28 +361,29 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
struct nand_chip *this = mtd->priv;
struct fsmc_nand_data *host = container_of(mtd,
struct fsmc_nand_data, mtd);
- struct fsmc_regs *regs = host->regs_va;
+ void *__iomem *regs = host->regs_va;
unsigned int bank = host->bank;
if (ctrl & NAND_CTRL_CHANGE) {
+ u32 pc;
+
if (ctrl & NAND_CLE) {
- this->IO_ADDR_R = (void __iomem *)host->cmd_va;
- this->IO_ADDR_W = (void __iomem *)host->cmd_va;
+ this->IO_ADDR_R = host->cmd_va;
+ this->IO_ADDR_W = host->cmd_va;
} else if (ctrl & NAND_ALE) {
- this->IO_ADDR_R = (void __iomem *)host->addr_va;
- this->IO_ADDR_W = (void __iomem *)host->addr_va;
+ this->IO_ADDR_R = host->addr_va;
+ this->IO_ADDR_W = host->addr_va;
} else {
- this->IO_ADDR_R = (void __iomem *)host->data_va;
- this->IO_ADDR_W = (void __iomem *)host->data_va;
+ this->IO_ADDR_R = host->data_va;
+ this->IO_ADDR_W = host->data_va;
}
- if (ctrl & NAND_NCE) {
- writel(readl(&regs->bank_regs[bank].pc) | FSMC_ENABLE,
- &regs->bank_regs[bank].pc);
- } else {
- writel(readl(&regs->bank_regs[bank].pc) & ~FSMC_ENABLE,
- &regs->bank_regs[bank].pc);
- }
+ pc = readl(FSMC_NAND_REG(regs, bank, PC));
+ if (ctrl & NAND_NCE)
+ pc |= FSMC_ENABLE;
+ else
+ pc &= ~FSMC_ENABLE;
+ writel(pc, FSMC_NAND_REG(regs, bank, PC));
}
mb();
@@ -287,22 +398,42 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
* This routine initializes timing parameters related to NAND memory access in
* FSMC registers
*/
-static void __init fsmc_nand_setup(struct fsmc_regs *regs, uint32_t bank,
- uint32_t busw)
+static void fsmc_nand_setup(void __iomem *regs, uint32_t bank,
+ uint32_t busw, struct fsmc_nand_timings *timings)
{
uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
+ uint32_t tclr, tar, thiz, thold, twait, tset;
+ struct fsmc_nand_timings *tims;
+ struct fsmc_nand_timings default_timings = {
+ .tclr = FSMC_TCLR_1,
+ .tar = FSMC_TAR_1,
+ .thiz = FSMC_THIZ_1,
+ .thold = FSMC_THOLD_4,
+ .twait = FSMC_TWAIT_6,
+ .tset = FSMC_TSET_0,
+ };
+
+ if (timings)
+ tims = timings;
+ else
+ tims = &default_timings;
+
+ tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT;
+ tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT;
+ thiz = (tims->thiz & FSMC_THIZ_MASK) << FSMC_THIZ_SHIFT;
+ thold = (tims->thold & FSMC_THOLD_MASK) << FSMC_THOLD_SHIFT;
+ twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT;
+ tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT;
if (busw)
- writel(value | FSMC_DEVWID_16, &regs->bank_regs[bank].pc);
+ writel(value | FSMC_DEVWID_16, FSMC_NAND_REG(regs, bank, PC));
else
- writel(value | FSMC_DEVWID_8, &regs->bank_regs[bank].pc);
-
- writel(readl(&regs->bank_regs[bank].pc) | FSMC_TCLR_1 | FSMC_TAR_1,
- &regs->bank_regs[bank].pc);
- writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
- &regs->bank_regs[bank].comm);
- writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
- &regs->bank_regs[bank].attrib);
+ writel(value | FSMC_DEVWID_8, FSMC_NAND_REG(regs, bank, PC));
+
+ writel(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar,
+ FSMC_NAND_REG(regs, bank, PC));
+ writel(thiz | thold | twait | tset, FSMC_NAND_REG(regs, bank, COMM));
+ writel(thiz | thold | twait | tset, FSMC_NAND_REG(regs, bank, ATTRIB));
}
/*
@@ -312,15 +443,15 @@ static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct fsmc_nand_data *host = container_of(mtd,
struct fsmc_nand_data, mtd);
- struct fsmc_regs *regs = host->regs_va;
+ void __iomem *regs = host->regs_va;
uint32_t bank = host->bank;
- writel(readl(&regs->bank_regs[bank].pc) & ~FSMC_ECCPLEN_256,
- &regs->bank_regs[bank].pc);
- writel(readl(&regs->bank_regs[bank].pc) & ~FSMC_ECCEN,
- &regs->bank_regs[bank].pc);
- writel(readl(&regs->bank_regs[bank].pc) | FSMC_ECCEN,
- &regs->bank_regs[bank].pc);
+ writel(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256,
+ FSMC_NAND_REG(regs, bank, PC));
+ writel(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN,
+ FSMC_NAND_REG(regs, bank, PC));
+ writel(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN,
+ FSMC_NAND_REG(regs, bank, PC));
}
/*
@@ -333,37 +464,42 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
{
struct fsmc_nand_data *host = container_of(mtd,
struct fsmc_nand_data, mtd);
- struct fsmc_regs *regs = host->regs_va;
+ void __iomem *regs = host->regs_va;
uint32_t bank = host->bank;
uint32_t ecc_tmp;
unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT;
do {
- if (readl(&regs->bank_regs[bank].sts) & FSMC_CODE_RDY)
+ if (readl(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY)
break;
else
cond_resched();
} while (!time_after_eq(jiffies, deadline));
- ecc_tmp = readl(&regs->bank_regs[bank].ecc1);
+ if (time_after_eq(jiffies, deadline)) {
+ dev_err(host->dev, "calculate ecc timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC1));
ecc[0] = (uint8_t) (ecc_tmp >> 0);
ecc[1] = (uint8_t) (ecc_tmp >> 8);
ecc[2] = (uint8_t) (ecc_tmp >> 16);
ecc[3] = (uint8_t) (ecc_tmp >> 24);
- ecc_tmp = readl(&regs->bank_regs[bank].ecc2);
+ ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC2));
ecc[4] = (uint8_t) (ecc_tmp >> 0);
ecc[5] = (uint8_t) (ecc_tmp >> 8);
ecc[6] = (uint8_t) (ecc_tmp >> 16);
ecc[7] = (uint8_t) (ecc_tmp >> 24);
- ecc_tmp = readl(&regs->bank_regs[bank].ecc3);
+ ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC3));
ecc[8] = (uint8_t) (ecc_tmp >> 0);
ecc[9] = (uint8_t) (ecc_tmp >> 8);
ecc[10] = (uint8_t) (ecc_tmp >> 16);
ecc[11] = (uint8_t) (ecc_tmp >> 24);
- ecc_tmp = readl(&regs->bank_regs[bank].sts);
+ ecc_tmp = readl(FSMC_NAND_REG(regs, bank, STS));
ecc[12] = (uint8_t) (ecc_tmp >> 16);
return 0;
@@ -379,11 +515,11 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
{
struct fsmc_nand_data *host = container_of(mtd,
struct fsmc_nand_data, mtd);
- struct fsmc_regs *regs = host->regs_va;
+ void __iomem *regs = host->regs_va;
uint32_t bank = host->bank;
uint32_t ecc_tmp;
- ecc_tmp = readl(&regs->bank_regs[bank].ecc1);
+ ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC1));
ecc[0] = (uint8_t) (ecc_tmp >> 0);
ecc[1] = (uint8_t) (ecc_tmp >> 8);
ecc[2] = (uint8_t) (ecc_tmp >> 16);
@@ -391,6 +527,166 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
return 0;
}
+/* Count the number of 0's in buff upto a max of max_bits */
+static int count_written_bits(uint8_t *buff, int size, int max_bits)
+{
+ int k, written_bits = 0;
+
+ for (k = 0; k < size; k++) {
+ written_bits += hweight8(~buff[k]);
+ if (written_bits > max_bits)
+ break;
+ }
+
+ return written_bits;
+}
+
+static void dma_complete(void *param)
+{
+ struct fsmc_nand_data *host = param;
+
+ complete(&host->dma_access_complete);
+}
+
+static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
+ enum dma_data_direction direction)
+{
+ struct dma_chan *chan;
+ struct dma_device *dma_dev;
+ struct dma_async_tx_descriptor *tx;
+ dma_addr_t dma_dst, dma_src, dma_addr;
+ dma_cookie_t cookie;
+ unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
+ int ret;
+
+ if (direction == DMA_TO_DEVICE)
+ chan = host->write_dma_chan;
+ else if (direction == DMA_FROM_DEVICE)
+ chan = host->read_dma_chan;
+ else
+ return -EINVAL;
+
+ dma_dev = chan->device;
+ dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction);
+
+ if (direction == DMA_TO_DEVICE) {
+ dma_src = dma_addr;
+ dma_dst = host->data_pa;
+ flags |= DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_SKIP_DEST_UNMAP;
+ } else {
+ dma_src = host->data_pa;
+ dma_dst = dma_addr;
+ flags |= DMA_COMPL_DEST_UNMAP_SINGLE | DMA_COMPL_SKIP_SRC_UNMAP;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src,
+ len, flags);
+
+ if (!tx) {
+ dev_err(host->dev, "device_prep_dma_memcpy error\n");
+ dma_unmap_single(dma_dev->dev, dma_addr, len, direction);
+ return -EIO;
+ }
+
+ tx->callback = dma_complete;
+ tx->callback_param = host;
+ cookie = tx->tx_submit(tx);
+
+ ret = dma_submit_error(cookie);
+ if (ret) {
+ dev_err(host->dev, "dma_submit_error %d\n", cookie);
+ return ret;
+ }
+
+ dma_async_issue_pending(chan);
+
+ ret =
+ wait_for_completion_interruptible_timeout(&host->dma_access_complete,
+ msecs_to_jiffies(3000));
+ if (ret <= 0) {
+ chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+ dev_err(host->dev, "wait_for_completion_timeout\n");
+ return ret ? ret : -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/*
+ * fsmc_write_buf - write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *chip = mtd->priv;
+
+ if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
+ IS_ALIGNED(len, sizeof(uint32_t))) {
+ uint32_t *p = (uint32_t *)buf;
+ len = len >> 2;
+ for (i = 0; i < len; i++)
+ writel(p[i], chip->IO_ADDR_W);
+ } else {
+ for (i = 0; i < len; i++)
+ writeb(buf[i], chip->IO_ADDR_W);
+ }
+}
+
+/*
+ * fsmc_read_buf - read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *chip = mtd->priv;
+
+ if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
+ IS_ALIGNED(len, sizeof(uint32_t))) {
+ uint32_t *p = (uint32_t *)buf;
+ len = len >> 2;
+ for (i = 0; i < len; i++)
+ p[i] = readl(chip->IO_ADDR_R);
+ } else {
+ for (i = 0; i < len; i++)
+ buf[i] = readb(chip->IO_ADDR_R);
+ }
+}
+
+/*
+ * fsmc_read_buf_dma - read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct fsmc_nand_data *host;
+
+ host = container_of(mtd, struct fsmc_nand_data, mtd);
+ dma_xfer(host, buf, len, DMA_FROM_DEVICE);
+}
+
+/*
+ * fsmc_write_buf_dma - write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct fsmc_nand_data *host;
+
+ host = container_of(mtd, struct fsmc_nand_data, mtd);
+ dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE);
+}
+
/*
* fsmc_read_page_hwecc
* @mtd: mtd info structure
@@ -426,7 +722,6 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *oob = (uint8_t *)&ecc_oob[0];
for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
-
chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page);
chip->ecc.hwctl(mtd, NAND_ECC_READ);
chip->read_buf(mtd, p, eccsize);
@@ -437,17 +732,19 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
group++;
/*
- * length is intentionally kept a higher multiple of 2
- * to read at least 13 bytes even in case of 16 bit NAND
- * devices
- */
- len = roundup(len, 2);
+ * length is intentionally kept a higher multiple of 2
+ * to read at least 13 bytes even in case of 16 bit NAND
+ * devices
+ */
+ if (chip->options & NAND_BUSWIDTH_16)
+ len = roundup(len, 2);
+
chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page);
chip->read_buf(mtd, oob + j, len);
j += len;
}
- memcpy(&ecc_code[i], oob, 13);
+ memcpy(&ecc_code[i], oob, chip->ecc.bytes);
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
@@ -461,7 +758,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
}
/*
- * fsmc_correct_data
+ * fsmc_bch8_correct_data
* @mtd: mtd info structure
* @dat: buffer of read data
* @read_ecc: ecc read from device spare area
@@ -470,19 +767,51 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
* calc_ecc is a 104 bit information containing maximum of 8 error
* offset informations of 13 bits each in 512 bytes of read data.
*/
-static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat,
+static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
struct fsmc_nand_data *host = container_of(mtd,
struct fsmc_nand_data, mtd);
- struct fsmc_regs *regs = host->regs_va;
+ struct nand_chip *chip = mtd->priv;
+ void __iomem *regs = host->regs_va;
unsigned int bank = host->bank;
- uint16_t err_idx[8];
- uint64_t ecc_data[2];
+ uint32_t err_idx[8];
uint32_t num_err, i;
+ uint32_t ecc1, ecc2, ecc3, ecc4;
+
+ num_err = (readl(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF;
+
+ /* no bit flipping */
+ if (likely(num_err == 0))
+ return 0;
+
+ /* too many errors */
+ if (unlikely(num_err > 8)) {
+ /*
+ * This is a temporary erase check. A newly erased page read
+ * would result in an ecc error because the oob data is also
+ * erased to FF and the calculated ecc for an FF data is not
+ * FF..FF.
+ * This is a workaround to skip performing correction in case
+ * data is FF..FF
+ *
+ * Logic:
+ * For every page, each bit written as 0 is counted until these
+ * number of bits are greater than 8 (the maximum correction
+ * capability of FSMC for each 512 + 13 bytes)
+ */
+
+ int bits_ecc = count_written_bits(read_ecc, chip->ecc.bytes, 8);
+ int bits_data = count_written_bits(dat, chip->ecc.size, 8);
+
+ if ((bits_ecc + bits_data) <= 8) {
+ if (bits_data)
+ memset(dat, 0xff, chip->ecc.size);
+ return bits_data;
+ }
- /* The calculated ecc is actually the correction index in data */
- memcpy(ecc_data, calc_ecc, 13);
+ return -EBADMSG;
+ }
/*
* ------------------- calc_ecc[] bit wise -----------|--13 bits--|
@@ -493,27 +822,26 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat,
* uint64_t array and error offset indexes are populated in err_idx
* array
*/
- for (i = 0; i < 8; i++) {
- if (i == 4) {
- err_idx[4] = ((ecc_data[1] & 0x1) << 12) | ecc_data[0];
- ecc_data[1] >>= 1;
- continue;
- }
- err_idx[i] = (ecc_data[i/4] & 0x1FFF);
- ecc_data[i/4] >>= 13;
- }
-
- num_err = (readl(&regs->bank_regs[bank].sts) >> 10) & 0xF;
-
- if (num_err == 0xF)
- return -EBADMSG;
+ ecc1 = readl(FSMC_NAND_REG(regs, bank, ECC1));
+ ecc2 = readl(FSMC_NAND_REG(regs, bank, ECC2));
+ ecc3 = readl(FSMC_NAND_REG(regs, bank, ECC3));
+ ecc4 = readl(FSMC_NAND_REG(regs, bank, STS));
+
+ err_idx[0] = (ecc1 >> 0) & 0x1FFF;
+ err_idx[1] = (ecc1 >> 13) & 0x1FFF;
+ err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F);
+ err_idx[3] = (ecc2 >> 7) & 0x1FFF;
+ err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF);
+ err_idx[5] = (ecc3 >> 1) & 0x1FFF;
+ err_idx[6] = (ecc3 >> 14) & 0x1FFF;
+ err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F);
i = 0;
while (num_err--) {
change_bit(0, (unsigned long *)&err_idx[i]);
change_bit(1, (unsigned long *)&err_idx[i]);
- if (err_idx[i] <= 512 * 8) {
+ if (err_idx[i] < chip->ecc.size * 8) {
change_bit(err_idx[i], (unsigned long *)dat);
i++;
}
@@ -521,6 +849,44 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat,
return i;
}
+static bool filter(struct dma_chan *chan, void *slave)
+{
+ chan->private = slave;
+ return true;
+}
+
+#ifdef CONFIG_OF
+static int __devinit fsmc_nand_probe_config_dt(struct platform_device *pdev,
+ struct device_node *np)
+{
+ struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ u32 val;
+
+ /* Set default NAND width to 8 bits */
+ pdata->width = 8;
+ if (!of_property_read_u32(np, "bank-width", &val)) {
+ if (val == 2) {
+ pdata->width = 16;
+ } else if (val != 1) {
+ dev_err(&pdev->dev, "invalid bank-width %u\n", val);
+ return -EINVAL;
+ }
+ }
+ of_property_read_u32(np, "st,ale-off", &pdata->ale_off);
+ of_property_read_u32(np, "st,cle-off", &pdata->cle_off);
+ if (of_get_property(np, "nand-skip-bbtscan", NULL))
+ pdata->options = NAND_SKIP_BBTSCAN;
+
+ return 0;
+}
+#else
+static int __devinit fsmc_nand_probe_config_dt(struct platform_device *pdev,
+ struct device_node *np)
+{
+ return -ENOSYS;
+}
+#endif
+
/*
* fsmc_nand_probe - Probe function
* @pdev: platform device structure
@@ -528,102 +894,109 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat,
static int __init fsmc_nand_probe(struct platform_device *pdev)
{
struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node __maybe_unused *np = pdev->dev.of_node;
+ struct mtd_part_parser_data ppdata = {};
struct fsmc_nand_data *host;
struct mtd_info *mtd;
struct nand_chip *nand;
- struct fsmc_regs *regs;
struct resource *res;
+ dma_cap_mask_t mask;
int ret = 0;
u32 pid;
int i;
+ if (np) {
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ pdev->dev.platform_data = pdata;
+ ret = fsmc_nand_probe_config_dt(pdev, np);
+ if (ret) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -ENODEV;
+ }
+ }
+
if (!pdata) {
dev_err(&pdev->dev, "platform data is NULL\n");
return -EINVAL;
}
/* Allocate memory for the device structure (and zero it) */
- host = kzalloc(sizeof(*host), GFP_KERNEL);
+ host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
if (!host) {
dev_err(&pdev->dev, "failed to allocate device structure\n");
return -ENOMEM;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
- if (!res) {
- ret = -EIO;
- goto err_probe1;
- }
+ if (!res)
+ return -EINVAL;
- host->resdata = request_mem_region(res->start, resource_size(res),
- pdev->name);
- if (!host->resdata) {
- ret = -EIO;
- goto err_probe1;
+ if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+ pdev->name)) {
+ dev_err(&pdev->dev, "Failed to get memory data resourse\n");
+ return -ENOENT;
}
- host->data_va = ioremap(res->start, resource_size(res));
+ host->data_pa = (dma_addr_t)res->start;
+ host->data_va = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
if (!host->data_va) {
- ret = -EIO;
- goto err_probe1;
+ dev_err(&pdev->dev, "data ioremap failed\n");
+ return -ENOMEM;
}
- host->resaddr = request_mem_region(res->start + PLAT_NAND_ALE,
- resource_size(res), pdev->name);
- if (!host->resaddr) {
- ret = -EIO;
- goto err_probe1;
+ if (!devm_request_mem_region(&pdev->dev, res->start + pdata->ale_off,
+ resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "Failed to get memory ale resourse\n");
+ return -ENOENT;
}
- host->addr_va = ioremap(res->start + PLAT_NAND_ALE, resource_size(res));
+ host->addr_va = devm_ioremap(&pdev->dev, res->start + pdata->ale_off,
+ resource_size(res));
if (!host->addr_va) {
- ret = -EIO;
- goto err_probe1;
+ dev_err(&pdev->dev, "ale ioremap failed\n");
+ return -ENOMEM;
}
- host->rescmd = request_mem_region(res->start + PLAT_NAND_CLE,
- resource_size(res), pdev->name);
- if (!host->rescmd) {
- ret = -EIO;
- goto err_probe1;
+ if (!devm_request_mem_region(&pdev->dev, res->start + pdata->cle_off,
+ resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "Failed to get memory cle resourse\n");
+ return -ENOENT;
}
- host->cmd_va = ioremap(res->start + PLAT_NAND_CLE, resource_size(res));
+ host->cmd_va = devm_ioremap(&pdev->dev, res->start + pdata->cle_off,
+ resource_size(res));
if (!host->cmd_va) {
- ret = -EIO;
- goto err_probe1;
+ dev_err(&pdev->dev, "ale ioremap failed\n");
+ return -ENOMEM;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
- if (!res) {
- ret = -EIO;
- goto err_probe1;
- }
+ if (!res)
+ return -EINVAL;
- host->resregs = request_mem_region(res->start, resource_size(res),
- pdev->name);
- if (!host->resregs) {
- ret = -EIO;
- goto err_probe1;
+ if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+ pdev->name)) {
+ dev_err(&pdev->dev, "Failed to get memory regs resourse\n");
+ return -ENOENT;
}
- host->regs_va = ioremap(res->start, resource_size(res));
+ host->regs_va = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
if (!host->regs_va) {
- ret = -EIO;
- goto err_probe1;
+ dev_err(&pdev->dev, "regs ioremap failed\n");
+ return -ENOMEM;
}
host->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk)) {
dev_err(&pdev->dev, "failed to fetch block clock\n");
- ret = PTR_ERR(host->clk);
- host->clk = NULL;
- goto err_probe1;
+ return PTR_ERR(host->clk);
}
ret = clk_enable(host->clk);
if (ret)
- goto err_probe1;
+ goto err_clk_enable;
/*
* This device ID is actually a common AMBA ID as used on the
@@ -639,7 +1012,14 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
host->bank = pdata->bank;
host->select_chip = pdata->select_bank;
- regs = host->regs_va;
+ host->partitions = pdata->partitions;
+ host->nr_partitions = pdata->nr_partitions;
+ host->dev = &pdev->dev;
+ host->dev_timings = pdata->nand_timings;
+ host->mode = pdata->mode;
+
+ if (host->mode == USE_DMA_ACCESS)
+ init_completion(&host->dma_access_complete);
/* Link all private pointers */
mtd = &host->mtd;
@@ -658,21 +1038,53 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
nand->ecc.size = 512;
nand->options = pdata->options;
nand->select_chip = fsmc_select_chip;
+ nand->badblockbits = 7;
if (pdata->width == FSMC_NAND_BW16)
nand->options |= NAND_BUSWIDTH_16;
- fsmc_nand_setup(regs, host->bank, nand->options & NAND_BUSWIDTH_16);
+ switch (host->mode) {
+ case USE_DMA_ACCESS:
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->read_dma_chan = dma_request_channel(mask, filter,
+ pdata->read_dma_priv);
+ if (!host->read_dma_chan) {
+ dev_err(&pdev->dev, "Unable to get read dma channel\n");
+ goto err_req_read_chnl;
+ }
+ host->write_dma_chan = dma_request_channel(mask, filter,
+ pdata->write_dma_priv);
+ if (!host->write_dma_chan) {
+ dev_err(&pdev->dev, "Unable to get write dma channel\n");
+ goto err_req_write_chnl;
+ }
+ nand->read_buf = fsmc_read_buf_dma;
+ nand->write_buf = fsmc_write_buf_dma;
+ break;
+
+ default:
+ case USE_WORD_ACCESS:
+ nand->read_buf = fsmc_read_buf;
+ nand->write_buf = fsmc_write_buf;
+ break;
+ }
+
+ fsmc_nand_setup(host->regs_va, host->bank,
+ nand->options & NAND_BUSWIDTH_16,
+ host->dev_timings);
if (AMBA_REV_BITS(host->pid) >= 8) {
nand->ecc.read_page = fsmc_read_page_hwecc;
nand->ecc.calculate = fsmc_read_hwecc_ecc4;
- nand->ecc.correct = fsmc_correct_data;
+ nand->ecc.correct = fsmc_bch8_correct_data;
nand->ecc.bytes = 13;
+ nand->ecc.strength = 8;
} else {
nand->ecc.calculate = fsmc_read_hwecc_ecc1;
nand->ecc.correct = nand_correct_data;
nand->ecc.bytes = 3;
+ nand->ecc.strength = 1;
}
/*
@@ -681,19 +1093,52 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
if (nand_scan_ident(&host->mtd, 1, NULL)) {
ret = -ENXIO;
dev_err(&pdev->dev, "No NAND Device found!\n");
- goto err_probe;
+ goto err_scan_ident;
}
if (AMBA_REV_BITS(host->pid) >= 8) {
- if (host->mtd.writesize == 512) {
- nand->ecc.layout = &fsmc_ecc4_sp_layout;
+ switch (host->mtd.oobsize) {
+ case 16:
+ nand->ecc.layout = &fsmc_ecc4_16_layout;
host->ecc_place = &fsmc_ecc4_sp_place;
- } else {
- nand->ecc.layout = &fsmc_ecc4_lp_layout;
+ break;
+ case 64:
+ nand->ecc.layout = &fsmc_ecc4_64_layout;
+ host->ecc_place = &fsmc_ecc4_lp_place;
+ break;
+ case 128:
+ nand->ecc.layout = &fsmc_ecc4_128_layout;
+ host->ecc_place = &fsmc_ecc4_lp_place;
+ break;
+ case 224:
+ nand->ecc.layout = &fsmc_ecc4_224_layout;
host->ecc_place = &fsmc_ecc4_lp_place;
+ break;
+ case 256:
+ nand->ecc.layout = &fsmc_ecc4_256_layout;
+ host->ecc_place = &fsmc_ecc4_lp_place;
+ break;
+ default:
+ printk(KERN_WARNING "No oob scheme defined for "
+ "oobsize %d\n", mtd->oobsize);
+ BUG();
}
} else {
- nand->ecc.layout = &fsmc_ecc1_layout;
+ switch (host->mtd.oobsize) {
+ case 16:
+ nand->ecc.layout = &fsmc_ecc1_16_layout;
+ break;
+ case 64:
+ nand->ecc.layout = &fsmc_ecc1_64_layout;
+ break;
+ case 128:
+ nand->ecc.layout = &fsmc_ecc1_128_layout;
+ break;
+ default:
+ printk(KERN_WARNING "No oob scheme defined for "
+ "oobsize %d\n", mtd->oobsize);
+ BUG();
+ }
}
/* Second stage of scan to fill MTD data-structures */
@@ -713,13 +1158,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
* Check for partition info passed
*/
host->mtd.name = "nand";
- ret = mtd_device_parse_register(&host->mtd, NULL, 0,
- host->mtd.size <= 0x04000000 ?
- partition_info_16KB_blk :
- partition_info_128KB_blk,
- host->mtd.size <= 0x04000000 ?
- ARRAY_SIZE(partition_info_16KB_blk) :
- ARRAY_SIZE(partition_info_128KB_blk));
+ ppdata.of_node = np;
+ ret = mtd_device_parse_register(&host->mtd, NULL, &ppdata,
+ host->partitions, host->nr_partitions);
if (ret)
goto err_probe;
@@ -728,32 +1169,16 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
return 0;
err_probe:
+err_scan_ident:
+ if (host->mode == USE_DMA_ACCESS)
+ dma_release_channel(host->write_dma_chan);
+err_req_write_chnl:
+ if (host->mode == USE_DMA_ACCESS)
+ dma_release_channel(host->read_dma_chan);
+err_req_read_chnl:
clk_disable(host->clk);
-err_probe1:
- if (host->clk)
- clk_put(host->clk);
- if (host->regs_va)
- iounmap(host->regs_va);
- if (host->resregs)
- release_mem_region(host->resregs->start,
- resource_size(host->resregs));
- if (host->cmd_va)
- iounmap(host->cmd_va);
- if (host->rescmd)
- release_mem_region(host->rescmd->start,
- resource_size(host->rescmd));
- if (host->addr_va)
- iounmap(host->addr_va);
- if (host->resaddr)
- release_mem_region(host->resaddr->start,
- resource_size(host->resaddr));
- if (host->data_va)
- iounmap(host->data_va);
- if (host->resdata)
- release_mem_region(host->resdata->start,
- resource_size(host->resdata));
-
- kfree(host);
+err_clk_enable:
+ clk_put(host->clk);
return ret;
}
@@ -768,24 +1193,15 @@ static int fsmc_nand_remove(struct platform_device *pdev)
if (host) {
nand_release(&host->mtd);
+
+ if (host->mode == USE_DMA_ACCESS) {
+ dma_release_channel(host->write_dma_chan);
+ dma_release_channel(host->read_dma_chan);
+ }
clk_disable(host->clk);
clk_put(host->clk);
-
- iounmap(host->regs_va);
- release_mem_region(host->resregs->start,
- resource_size(host->resregs));
- iounmap(host->cmd_va);
- release_mem_region(host->rescmd->start,
- resource_size(host->rescmd));
- iounmap(host->addr_va);
- release_mem_region(host->resaddr->start,
- resource_size(host->resaddr));
- iounmap(host->data_va);
- release_mem_region(host->resdata->start,
- resource_size(host->resdata));
-
- kfree(host);
}
+
return 0;
}
@@ -801,15 +1217,24 @@ static int fsmc_nand_suspend(struct device *dev)
static int fsmc_nand_resume(struct device *dev)
{
struct fsmc_nand_data *host = dev_get_drvdata(dev);
- if (host)
+ if (host) {
clk_enable(host->clk);
+ fsmc_nand_setup(host->regs_va, host->bank,
+ host->nand.options & NAND_BUSWIDTH_16,
+ host->dev_timings);
+ }
return 0;
}
-static const struct dev_pm_ops fsmc_nand_pm_ops = {
- .suspend = fsmc_nand_suspend,
- .resume = fsmc_nand_resume,
+static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id fsmc_nand_id_table[] = {
+ { .compatible = "st,spear600-fsmc-nand" },
+ {}
};
+MODULE_DEVICE_TABLE(of, fsmc_nand_id_table);
#endif
static struct platform_driver fsmc_nand_driver = {
@@ -817,6 +1242,7 @@ static struct platform_driver fsmc_nand_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "fsmc-nand",
+ .of_match_table = of_match_ptr(fsmc_nand_id_table),
#ifdef CONFIG_PM
.pm = &fsmc_nand_pm_ops,
#endif
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
index 7db6555..e8ea710 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
@@ -835,7 +835,7 @@ int gpmi_send_command(struct gpmi_nand_data *this)
| BM_GPMI_CTRL0_ADDRESS_INCREMENT
| BF_GPMI_CTRL0_XFER_COUNT(this->command_length);
pio[1] = pio[2] = 0;
- desc = channel->device->device_prep_slave_sg(channel,
+ desc = dmaengine_prep_slave_sg(channel,
(struct scatterlist *)pio,
ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
if (!desc) {
@@ -848,8 +848,10 @@ int gpmi_send_command(struct gpmi_nand_data *this)
sg_init_one(sgl, this->cmd_buffer, this->command_length);
dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE);
- desc = channel->device->device_prep_slave_sg(channel,
- sgl, 1, DMA_MEM_TO_DEV, 1);
+ desc = dmaengine_prep_slave_sg(channel,
+ sgl, 1, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
if (!desc) {
pr_err("step 2 error\n");
return -1;
@@ -880,8 +882,7 @@ int gpmi_send_data(struct gpmi_nand_data *this)
| BF_GPMI_CTRL0_ADDRESS(address)
| BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
pio[1] = 0;
- desc = channel->device->device_prep_slave_sg(channel,
- (struct scatterlist *)pio,
+ desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio,
ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
if (!desc) {
pr_err("step 1 error\n");
@@ -890,8 +891,9 @@ int gpmi_send_data(struct gpmi_nand_data *this)
/* [2] send DMA request */
prepare_data_dma(this, DMA_TO_DEVICE);
- desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl,
- 1, DMA_MEM_TO_DEV, 1);
+ desc = dmaengine_prep_slave_sg(channel, &this->data_sgl,
+ 1, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
pr_err("step 2 error\n");
return -1;
@@ -916,7 +918,7 @@ int gpmi_read_data(struct gpmi_nand_data *this)
| BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA)
| BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
pio[1] = 0;
- desc = channel->device->device_prep_slave_sg(channel,
+ desc = dmaengine_prep_slave_sg(channel,
(struct scatterlist *)pio,
ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
if (!desc) {
@@ -926,8 +928,9 @@ int gpmi_read_data(struct gpmi_nand_data *this)
/* [2] : send DMA request */
prepare_data_dma(this, DMA_FROM_DEVICE);
- desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl,
- 1, DMA_DEV_TO_MEM, 1);
+ desc = dmaengine_prep_slave_sg(channel, &this->data_sgl,
+ 1, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
pr_err("step 2 error\n");
return -1;
@@ -972,9 +975,10 @@ int gpmi_send_page(struct gpmi_nand_data *this,
pio[4] = payload;
pio[5] = auxiliary;
- desc = channel->device->device_prep_slave_sg(channel,
+ desc = dmaengine_prep_slave_sg(channel,
(struct scatterlist *)pio,
- ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
+ ARRAY_SIZE(pio), DMA_TRANS_NONE,
+ DMA_CTRL_ACK);
if (!desc) {
pr_err("step 2 error\n");
return -1;
@@ -1007,7 +1011,7 @@ int gpmi_read_page(struct gpmi_nand_data *this,
| BF_GPMI_CTRL0_ADDRESS(address)
| BF_GPMI_CTRL0_XFER_COUNT(0);
pio[1] = 0;
- desc = channel->device->device_prep_slave_sg(channel,
+ desc = dmaengine_prep_slave_sg(channel,
(struct scatterlist *)pio, 2,
DMA_TRANS_NONE, 0);
if (!desc) {
@@ -1036,9 +1040,10 @@ int gpmi_read_page(struct gpmi_nand_data *this,
pio[3] = geo->page_size;
pio[4] = payload;
pio[5] = auxiliary;
- desc = channel->device->device_prep_slave_sg(channel,
+ desc = dmaengine_prep_slave_sg(channel,
(struct scatterlist *)pio,
- ARRAY_SIZE(pio), DMA_TRANS_NONE, 1);
+ ARRAY_SIZE(pio), DMA_TRANS_NONE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
pr_err("step 2 error\n");
return -1;
@@ -1055,9 +1060,11 @@ int gpmi_read_page(struct gpmi_nand_data *this,
| BF_GPMI_CTRL0_ADDRESS(address)
| BF_GPMI_CTRL0_XFER_COUNT(geo->page_size);
pio[1] = 0;
- desc = channel->device->device_prep_slave_sg(channel,
- (struct scatterlist *)pio, 2,
- DMA_TRANS_NONE, 1);
+ pio[2] = 0; /* clear GPMI_HW_GPMI_ECCCTRL, disable the BCH. */
+ desc = dmaengine_prep_slave_sg(channel,
+ (struct scatterlist *)pio, 3,
+ DMA_TRANS_NONE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
pr_err("step 3 error\n");
return -1;
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index 493ec2f..75b1dde 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -1124,7 +1124,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
/* Do we have a flash based bad block table ? */
- if (chip->options & NAND_BBT_USE_FLASH)
+ if (chip->bbt_options & NAND_BBT_USE_FLASH)
ret = nand_update_bbt(mtd, ofs);
else {
chipnr = (int)(ofs >> chip->chip_shift);
@@ -1155,7 +1155,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
return ret;
}
-static int __devinit nand_boot_set_geometry(struct gpmi_nand_data *this)
+static int nand_boot_set_geometry(struct gpmi_nand_data *this)
{
struct boot_rom_geometry *geometry = &this->rom_geometry;
@@ -1182,7 +1182,7 @@ static int __devinit nand_boot_set_geometry(struct gpmi_nand_data *this)
}
static const char *fingerprint = "STMP";
-static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this)
+static int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
{
struct boot_rom_geometry *rom_geo = &this->rom_geometry;
struct device *dev = this->dev;
@@ -1239,7 +1239,7 @@ static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this)
}
/* Writes a transcription stamp. */
-static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this)
+static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
{
struct device *dev = this->dev;
struct boot_rom_geometry *rom_geo = &this->rom_geometry;
@@ -1322,7 +1322,7 @@ static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this)
return 0;
}
-static int __devinit mx23_boot_init(struct gpmi_nand_data *this)
+static int mx23_boot_init(struct gpmi_nand_data *this)
{
struct device *dev = this->dev;
struct nand_chip *chip = &this->nand;
@@ -1391,7 +1391,7 @@ static int __devinit mx23_boot_init(struct gpmi_nand_data *this)
return 0;
}
-static int __devinit nand_boot_init(struct gpmi_nand_data *this)
+static int nand_boot_init(struct gpmi_nand_data *this)
{
nand_boot_set_geometry(this);
@@ -1401,7 +1401,7 @@ static int __devinit nand_boot_init(struct gpmi_nand_data *this)
return 0;
}
-static int __devinit gpmi_set_geometry(struct gpmi_nand_data *this)
+static int gpmi_set_geometry(struct gpmi_nand_data *this)
{
int ret;
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
index e023bcc..ec6180d 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
@@ -20,7 +20,7 @@
#include <linux/mtd/nand.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
-#include <mach/dma.h>
+#include <linux/fsl/mxs-dma.h>
struct resources {
void *gpmi_regs;
diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c
index 5dc6f0d..11e4878 100644
--- a/drivers/mtd/nand/h1910.c
+++ b/drivers/mtd/nand/h1910.c
@@ -135,8 +135,8 @@ static int __init h1910_init(void)
}
/* Register the partitions */
- mtd_device_parse_register(h1910_nand_mtd, NULL, 0,
- partition_info, NUM_PARTITIONS);
+ mtd_device_parse_register(h1910_nand_mtd, NULL, NULL, partition_info,
+ NUM_PARTITIONS);
/* Return happy */
return 0;
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c
index ac3b9f2..e4147e8 100644
--- a/drivers/mtd/nand/jz4740_nand.c
+++ b/drivers/mtd/nand/jz4740_nand.c
@@ -332,6 +332,11 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
chip->ecc.mode = NAND_ECC_HW_OOB_FIRST;
chip->ecc.size = 512;
chip->ecc.bytes = 9;
+ chip->ecc.strength = 2;
+ /*
+ * FIXME: ecc_strength value of 2 bits per 512 bytes of data is a
+ * conservative guess, given 9 ecc bytes and reed-solomon alg.
+ */
if (pdata)
chip->ecc.layout = pdata->ecc_layout;
@@ -367,9 +372,9 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
goto err_gpio_free;
}
- ret = mtd_device_parse_register(mtd, NULL, 0,
- pdata ? pdata->partitions : NULL,
- pdata ? pdata->num_partitions : 0);
+ ret = mtd_device_parse_register(mtd, NULL, NULL,
+ pdata ? pdata->partitions : NULL,
+ pdata ? pdata->num_partitions : 0);
if (ret) {
dev_err(&pdev->dev, "Failed to add mtd device\n");
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 74a43b8..cc0678a 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -1225,9 +1225,16 @@ static int __init mxcnd_probe(struct platform_device *pdev)
goto escan;
}
+ if (this->ecc.mode == NAND_ECC_HW) {
+ if (nfc_is_v1())
+ this->ecc.strength = 1;
+ else
+ this->ecc.strength = (host->eccsize == 4) ? 4 : 8;
+ }
+
/* Register the partitions */
- mtd_device_parse_register(mtd, part_probes, 0,
- pdata->parts, pdata->nr_parts);
+ mtd_device_parse_register(mtd, part_probes, NULL, pdata->parts,
+ pdata->nr_parts);
platform_set_drvdata(pdev, host);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 8a393f9..47b19c0 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -123,12 +123,6 @@ static int check_offs_len(struct mtd_info *mtd,
ret = -EINVAL;
}
- /* Do not allow past end of device */
- if (ofs + len > mtd->size) {
- pr_debug("%s: past end of device\n", __func__);
- ret = -EINVAL;
- }
-
return ret;
}
@@ -338,7 +332,7 @@ static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
*/
static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
{
- int page, chipnr, res = 0;
+ int page, chipnr, res = 0, i = 0;
struct nand_chip *chip = mtd->priv;
u16 bad;
@@ -356,23 +350,29 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
chip->select_chip(mtd, chipnr);
}
- if (chip->options & NAND_BUSWIDTH_16) {
- chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE,
- page);
- bad = cpu_to_le16(chip->read_word(mtd));
- if (chip->badblockpos & 0x1)
- bad >>= 8;
- else
- bad &= 0xFF;
- } else {
- chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
- bad = chip->read_byte(mtd);
- }
+ do {
+ if (chip->options & NAND_BUSWIDTH_16) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB,
+ chip->badblockpos & 0xFE, page);
+ bad = cpu_to_le16(chip->read_word(mtd));
+ if (chip->badblockpos & 0x1)
+ bad >>= 8;
+ else
+ bad &= 0xFF;
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos,
+ page);
+ bad = chip->read_byte(mtd);
+ }
- if (likely(chip->badblockbits == 8))
- res = bad != 0xFF;
- else
- res = hweight8(bad) < chip->badblockbits;
+ if (likely(chip->badblockbits == 8))
+ res = bad != 0xFF;
+ else
+ res = hweight8(bad) < chip->badblockbits;
+ ofs += mtd->writesize;
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+ i++;
+ } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
if (getchip)
nand_release_device(mtd);
@@ -386,51 +386,79 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
* @ofs: offset from device start
*
* This is the default implementation, which can be overridden by a hardware
- * specific driver.
+ * specific driver. We try operations in the following order, according to our
+ * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH):
+ * (1) erase the affected block, to allow OOB marker to be written cleanly
+ * (2) update in-memory BBT
+ * (3) write bad block marker to OOB area of affected block
+ * (4) update flash-based BBT
+ * Note that we retain the first error encountered in (3) or (4), finish the
+ * procedures, and dump the error in the end.
*/
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
struct nand_chip *chip = mtd->priv;
uint8_t buf[2] = { 0, 0 };
- int block, ret, i = 0;
+ int block, res, ret = 0, i = 0;
+ int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM);
- if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
- ofs += mtd->erasesize - mtd->writesize;
+ if (write_oob) {
+ struct erase_info einfo;
+
+ /* Attempt erase before marking OOB */
+ memset(&einfo, 0, sizeof(einfo));
+ einfo.mtd = mtd;
+ einfo.addr = ofs;
+ einfo.len = 1 << chip->phys_erase_shift;
+ nand_erase_nand(mtd, &einfo, 0);
+ }
/* Get block number */
block = (int)(ofs >> chip->bbt_erase_shift);
+ /* Mark block bad in memory-based BBT */
if (chip->bbt)
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
- /* Do we have a flash based bad block table? */
- if (chip->bbt_options & NAND_BBT_USE_FLASH)
- ret = nand_update_bbt(mtd, ofs);
- else {
+ /* Write bad block marker to OOB */
+ if (write_oob) {
struct mtd_oob_ops ops;
+ loff_t wr_ofs = ofs;
nand_get_device(chip, mtd, FL_WRITING);
- /*
- * Write to first two pages if necessary. If we write to more
- * than one location, the first error encountered quits the
- * procedure. We write two bytes per location, so we dont have
- * to mess with 16 bit access.
- */
- ops.len = ops.ooblen = 2;
ops.datbuf = NULL;
ops.oobbuf = buf;
- ops.ooboffs = chip->badblockpos & ~0x01;
+ ops.ooboffs = chip->badblockpos;
+ if (chip->options & NAND_BUSWIDTH_16) {
+ ops.ooboffs &= ~0x01;
+ ops.len = ops.ooblen = 2;
+ } else {
+ ops.len = ops.ooblen = 1;
+ }
ops.mode = MTD_OPS_PLACE_OOB;
+
+ /* Write to first/last page(s) if necessary */
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+ wr_ofs += mtd->erasesize - mtd->writesize;
do {
- ret = nand_do_write_oob(mtd, ofs, &ops);
+ res = nand_do_write_oob(mtd, wr_ofs, &ops);
+ if (!ret)
+ ret = res;
i++;
- ofs += mtd->writesize;
- } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) &&
- i < 2);
+ wr_ofs += mtd->writesize;
+ } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
nand_release_device(mtd);
}
+
+ /* Update flash-based bad block table */
+ if (chip->bbt_options & NAND_BBT_USE_FLASH) {
+ res = nand_update_bbt(mtd, ofs);
+ if (!ret)
+ ret = res;
+ }
+
if (!ret)
mtd->ecc_stats.badblocks++;
@@ -1586,25 +1614,14 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
struct mtd_oob_ops ops;
int ret;
- /* Do not allow reads past end of device */
- if ((from + len) > mtd->size)
- return -EINVAL;
- if (!len)
- return 0;
-
nand_get_device(chip, mtd, FL_READING);
-
ops.len = len;
ops.datbuf = buf;
ops.oobbuf = NULL;
ops.mode = 0;
-
ret = nand_do_read_ops(mtd, from, &ops);
-
*retlen = ops.retlen;
-
nand_release_device(mtd);
-
return ret;
}
@@ -2293,12 +2310,6 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
struct mtd_oob_ops ops;
int ret;
- /* Do not allow reads past end of device */
- if ((to + len) > mtd->size)
- return -EINVAL;
- if (!len)
- return 0;
-
/* Wait for the device to get ready */
panic_nand_wait(mtd, chip, 400);
@@ -2333,25 +2344,14 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
struct mtd_oob_ops ops;
int ret;
- /* Do not allow reads past end of device */
- if ((to + len) > mtd->size)
- return -EINVAL;
- if (!len)
- return 0;
-
nand_get_device(chip, mtd, FL_WRITING);
-
ops.len = len;
ops.datbuf = (uint8_t *)buf;
ops.oobbuf = NULL;
ops.mode = 0;
-
ret = nand_do_write_ops(mtd, to, &ops);
-
*retlen = ops.retlen;
-
nand_release_device(mtd);
-
return ret;
}
@@ -2550,8 +2550,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
if (check_offs_len(mtd, instr->addr, instr->len))
return -EINVAL;
- instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
-
/* Grab the lock and see if the device is available */
nand_get_device(chip, mtd, FL_ERASING);
@@ -2715,10 +2713,6 @@ static void nand_sync(struct mtd_info *mtd)
*/
static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
{
- /* Check for invalid offset */
- if (offs > mtd->size)
- return -EINVAL;
-
return nand_block_checkbad(mtd, offs, 1, 0);
}
@@ -2857,7 +2851,6 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
return 0;
- pr_info("ONFI flash detected\n");
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
for (i = 0; i < 3; i++) {
chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
@@ -2898,7 +2891,8 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
mtd->writesize = le32_to_cpu(p->byte_per_page);
mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
- chip->chipsize = (uint64_t)le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
+ chip->chipsize = le32_to_cpu(p->blocks_per_lun);
+ chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
*busw = 0;
if (le16_to_cpu(p->features) & 1)
*busw = NAND_BUSWIDTH_16;
@@ -2907,6 +2901,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
chip->options |= (NAND_NO_READRDY |
NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
+ pr_info("ONFI flash detected\n");
return 1;
}
@@ -3238,6 +3233,10 @@ int nand_scan_tail(struct mtd_info *mtd)
int i;
struct nand_chip *chip = mtd->priv;
+ /* New bad blocks should be marked in OOB, flash-based BBT, or both */
+ BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
+ !(chip->bbt_options & NAND_BBT_USE_FLASH));
+
if (!(chip->options & NAND_OWN_BUFFERS))
chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
if (!chip->buffers)
@@ -3350,6 +3349,7 @@ int nand_scan_tail(struct mtd_info *mtd)
if (!chip->ecc.size)
chip->ecc.size = 256;
chip->ecc.bytes = 3;
+ chip->ecc.strength = 1;
break;
case NAND_ECC_SOFT_BCH:
@@ -3384,6 +3384,8 @@ int nand_scan_tail(struct mtd_info *mtd)
pr_warn("BCH ECC initialization failed!\n");
BUG();
}
+ chip->ecc.strength =
+ chip->ecc.bytes*8 / fls(8*chip->ecc.size);
break;
case NAND_ECC_NONE:
@@ -3397,6 +3399,7 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.write_oob = nand_write_oob_std;
chip->ecc.size = mtd->writesize;
chip->ecc.bytes = 0;
+ chip->ecc.strength = 0;
break;
default:
@@ -3461,25 +3464,26 @@ int nand_scan_tail(struct mtd_info *mtd)
mtd->type = MTD_NANDFLASH;
mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
MTD_CAP_NANDFLASH;
- mtd->erase = nand_erase;
- mtd->point = NULL;
- mtd->unpoint = NULL;
- mtd->read = nand_read;
- mtd->write = nand_write;
- mtd->panic_write = panic_nand_write;
- mtd->read_oob = nand_read_oob;
- mtd->write_oob = nand_write_oob;
- mtd->sync = nand_sync;
- mtd->lock = NULL;
- mtd->unlock = NULL;
- mtd->suspend = nand_suspend;
- mtd->resume = nand_resume;
- mtd->block_isbad = nand_block_isbad;
- mtd->block_markbad = nand_block_markbad;
+ mtd->_erase = nand_erase;
+ mtd->_point = NULL;
+ mtd->_unpoint = NULL;
+ mtd->_read = nand_read;
+ mtd->_write = nand_write;
+ mtd->_panic_write = panic_nand_write;
+ mtd->_read_oob = nand_read_oob;
+ mtd->_write_oob = nand_write_oob;
+ mtd->_sync = nand_sync;
+ mtd->_lock = NULL;
+ mtd->_unlock = NULL;
+ mtd->_suspend = nand_suspend;
+ mtd->_resume = nand_resume;
+ mtd->_block_isbad = nand_block_isbad;
+ mtd->_block_markbad = nand_block_markbad;
mtd->writebufsize = mtd->writesize;
- /* propagate ecc.layout to mtd_info */
+ /* propagate ecc info to mtd_info */
mtd->ecclayout = chip->ecc.layout;
+ mtd->ecc_strength = chip->ecc.strength * chip->ecc.steps;
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index ec68854..2b6f632 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -179,6 +179,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.size = 256;
chip->ecc.bytes = 3;
+ chip->ecc.strength = 1;
chip->priv = ndfc;
ndfc->mtd.priv = chip;
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index b3a883e..c2b0bba 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -1058,6 +1058,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
(pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) {
info->nand.ecc.bytes = 3;
info->nand.ecc.size = 512;
+ info->nand.ecc.strength = 1;
info->nand.ecc.calculate = omap_calculate_ecc;
info->nand.ecc.hwctl = omap_enable_hwecc;
info->nand.ecc.correct = omap_correct_data;
@@ -1101,8 +1102,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
goto out_release_mem_region;
}
- mtd_device_parse_register(&info->mtd, NULL, 0,
- pdata->parts, pdata->nr_parts);
+ mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts,
+ pdata->nr_parts);
platform_set_drvdata(pdev, &info->mtd);
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c
index 29f505a..1d3bfb2 100644
--- a/drivers/mtd/nand/orion_nand.c
+++ b/drivers/mtd/nand/orion_nand.c
@@ -129,8 +129,8 @@ static int __init orion_nand_probe(struct platform_device *pdev)
}
mtd->name = "orion_nand";
- ret = mtd_device_parse_register(mtd, NULL, 0,
- board->parts, board->nr_parts);
+ ret = mtd_device_parse_register(mtd, NULL, NULL, board->parts,
+ board->nr_parts);
if (ret) {
nand_release(mtd);
goto no_dev;
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c
index 7f2da69..6404e6e 100644
--- a/drivers/mtd/nand/plat_nand.c
+++ b/drivers/mtd/nand/plat_nand.c
@@ -99,8 +99,9 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
}
err = mtd_device_parse_register(&data->mtd,
- pdata->chip.part_probe_types, 0,
- pdata->chip.partitions, pdata->chip.nr_partitions);
+ pdata->chip.part_probe_types, NULL,
+ pdata->chip.partitions,
+ pdata->chip.nr_partitions);
if (!err)
return err;
diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c
index 7e52af5..0ddd90e 100644
--- a/drivers/mtd/nand/ppchameleonevb.c
+++ b/drivers/mtd/nand/ppchameleonevb.c
@@ -275,11 +275,10 @@ static int __init ppchameleonevb_init(void)
ppchameleon_mtd->name = "ppchameleon-nand";
/* Register the partitions */
- mtd_device_parse_register(ppchameleon_mtd, NULL, 0,
- ppchameleon_mtd->size == NAND_SMALL_SIZE ?
- partition_info_me :
- partition_info_hi,
- NUM_PARTITIONS);
+ mtd_device_parse_register(ppchameleon_mtd, NULL, NULL,
+ ppchameleon_mtd->size == NAND_SMALL_SIZE ?
+ partition_info_me : partition_info_hi,
+ NUM_PARTITIONS);
nand_evb_init:
/****************************
@@ -365,11 +364,10 @@ static int __init ppchameleonevb_init(void)
ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME;
/* Register the partitions */
- mtd_device_parse_register(ppchameleonevb_mtd, NULL, 0,
- ppchameleon_mtd->size == NAND_SMALL_SIZE ?
- partition_info_me :
- partition_info_hi,
- NUM_PARTITIONS);
+ mtd_device_parse_register(ppchameleonevb_mtd, NULL, NULL,
+ ppchameleon_mtd->size == NAND_SMALL_SIZE ?
+ partition_info_me : partition_info_hi,
+ NUM_PARTITIONS);
/* Return happy */
return 0;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 5c3d719..def50ca 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -1002,6 +1002,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
KEEP_CONFIG:
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.size = host->page_size;
+ chip->ecc.strength = 1;
chip->options = NAND_NO_AUTOINCR;
chip->options |= NAND_NO_READRDY;
@@ -1228,8 +1229,9 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
continue;
}
- ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0,
- pdata->parts[cs], pdata->nr_parts[cs]);
+ ret = mtd_device_parse_register(info->host[cs]->mtd, NULL,
+ NULL, pdata->parts[cs],
+ pdata->nr_parts[cs]);
if (!ret)
probe_success = 1;
}
diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c
index 769a4e0..c204018 100644
--- a/drivers/mtd/nand/r852.c
+++ b/drivers/mtd/nand/r852.c
@@ -891,6 +891,7 @@ int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
chip->ecc.mode = NAND_ECC_HW_SYNDROME;
chip->ecc.size = R852_DMA_LEN;
chip->ecc.bytes = SM_OOB_SIZE;
+ chip->ecc.strength = 2;
chip->ecc.hwctl = r852_ecc_hwctl;
chip->ecc.calculate = r852_ecc_calculate;
chip->ecc.correct = r852_ecc_correct;
diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c
index f309add..e55b5cf 100644
--- a/drivers/mtd/nand/rtc_from4.c
+++ b/drivers/mtd/nand/rtc_from4.c
@@ -527,6 +527,7 @@ static int __init rtc_from4_init(void)
this->ecc.mode = NAND_ECC_HW_SYNDROME;
this->ecc.size = 512;
this->ecc.bytes = 8;
+ this->ecc.strength = 3;
/* return the status of extra status and ECC checks */
this->errstat = rtc_from4_errstat;
/* set the nand_oobinfo to support FPGA H/W error detection */
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 868685d..91121f3 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -751,8 +751,8 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
if (set)
mtd->mtd.name = set->name;
- return mtd_device_parse_register(&mtd->mtd, NULL, 0,
- set->partitions, set->nr_partitions);
+ return mtd_device_parse_register(&mtd->mtd, NULL, NULL,
+ set->partitions, set->nr_partitions);
}
/**
@@ -823,6 +823,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
chip->ecc.correct = s3c2410_nand_correct_data;
chip->ecc.mode = NAND_ECC_HW;
+ chip->ecc.strength = 1;
switch (info->cpu_type) {
case TYPE_S3C2410:
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
index 93b1f74..e9b2b26 100644
--- a/drivers/mtd/nand/sh_flctl.c
+++ b/drivers/mtd/nand/sh_flctl.c
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
@@ -283,7 +284,7 @@ static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val)
{
struct sh_flctl *flctl = mtd_to_flctl(mtd);
- uint32_t flcmncr_val = readl(FLCMNCR(flctl)) & ~SEL_16BIT;
+ uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT;
uint32_t flcmdcr_val, addr_len_bytes = 0;
/* Set SNAND bit if page size is 2048byte */
@@ -303,6 +304,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va
break;
case NAND_CMD_READ0:
case NAND_CMD_READOOB:
+ case NAND_CMD_RNDOUT:
addr_len_bytes = flctl->rw_ADRCNT;
flcmdcr_val |= CDSRC_E;
if (flctl->chip.options & NAND_BUSWIDTH_16)
@@ -320,6 +322,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va
break;
case NAND_CMD_READID:
flcmncr_val &= ~SNAND_E;
+ flcmdcr_val |= CDSRC_E;
addr_len_bytes = ADRCNT_1;
break;
case NAND_CMD_STATUS:
@@ -513,6 +516,8 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
struct sh_flctl *flctl = mtd_to_flctl(mtd);
uint32_t read_cmd = 0;
+ pm_runtime_get_sync(&flctl->pdev->dev);
+
flctl->read_bytes = 0;
if (command != NAND_CMD_PAGEPROG)
flctl->index = 0;
@@ -525,7 +530,6 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
execmd_read_page_sector(mtd, page_addr);
break;
}
- empty_fifo(flctl);
if (flctl->page_size)
set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
| command);
@@ -547,7 +551,6 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
break;
}
- empty_fifo(flctl);
if (flctl->page_size) {
set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
| NAND_CMD_READ0);
@@ -559,15 +562,35 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
flctl->read_bytes = mtd->oobsize;
goto read_normal_exit;
+ case NAND_CMD_RNDOUT:
+ if (flctl->hwecc)
+ break;
+
+ if (flctl->page_size)
+ set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8)
+ | command);
+ else
+ set_cmd_regs(mtd, command, command);
+
+ set_addr(mtd, column, 0);
+
+ flctl->read_bytes = mtd->writesize + mtd->oobsize - column;
+ goto read_normal_exit;
+
case NAND_CMD_READID:
- empty_fifo(flctl);
set_cmd_regs(mtd, command, command);
- set_addr(mtd, 0, 0);
- flctl->read_bytes = 4;
+ /* READID is always performed using an 8-bit bus */
+ if (flctl->chip.options & NAND_BUSWIDTH_16)
+ column <<= 1;
+ set_addr(mtd, column, 0);
+
+ flctl->read_bytes = 8;
writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
+ empty_fifo(flctl);
start_translation(flctl);
- read_datareg(flctl, 0); /* read and end */
+ read_fiforeg(flctl, flctl->read_bytes, 0);
+ wait_completion(flctl);
break;
case NAND_CMD_ERASE1:
@@ -650,29 +673,55 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command,
default:
break;
}
- return;
+ goto runtime_exit;
read_normal_exit:
writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
+ empty_fifo(flctl);
start_translation(flctl);
read_fiforeg(flctl, flctl->read_bytes, 0);
wait_completion(flctl);
+runtime_exit:
+ pm_runtime_put_sync(&flctl->pdev->dev);
return;
}
static void flctl_select_chip(struct mtd_info *mtd, int chipnr)
{
struct sh_flctl *flctl = mtd_to_flctl(mtd);
- uint32_t flcmncr_val = readl(FLCMNCR(flctl));
+ int ret;
switch (chipnr) {
case -1:
- flcmncr_val &= ~CE0_ENABLE;
- writel(flcmncr_val, FLCMNCR(flctl));
+ flctl->flcmncr_base &= ~CE0_ENABLE;
+
+ pm_runtime_get_sync(&flctl->pdev->dev);
+ writel(flctl->flcmncr_base, FLCMNCR(flctl));
+
+ if (flctl->qos_request) {
+ dev_pm_qos_remove_request(&flctl->pm_qos);
+ flctl->qos_request = 0;
+ }
+
+ pm_runtime_put_sync(&flctl->pdev->dev);
break;
case 0:
- flcmncr_val |= CE0_ENABLE;
- writel(flcmncr_val, FLCMNCR(flctl));
+ flctl->flcmncr_base |= CE0_ENABLE;
+
+ if (!flctl->qos_request) {
+ ret = dev_pm_qos_add_request(&flctl->pdev->dev,
+ &flctl->pm_qos, 100);
+ if (ret < 0)
+ dev_err(&flctl->pdev->dev,
+ "PM QoS request failed: %d\n", ret);
+ flctl->qos_request = 1;
+ }
+
+ if (flctl->holden) {
+ pm_runtime_get_sync(&flctl->pdev->dev);
+ writel(HOLDEN, FLHOLDCR(flctl));
+ pm_runtime_put_sync(&flctl->pdev->dev);
+ }
break;
default:
BUG();
@@ -730,11 +779,6 @@ static int flctl_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
return 0;
}
-static void flctl_register_init(struct sh_flctl *flctl, unsigned long val)
-{
- writel(val, FLCMNCR(flctl));
-}
-
static int flctl_chip_init_tail(struct mtd_info *mtd)
{
struct sh_flctl *flctl = mtd_to_flctl(mtd);
@@ -781,13 +825,13 @@ static int flctl_chip_init_tail(struct mtd_info *mtd)
chip->ecc.size = 512;
chip->ecc.bytes = 10;
+ chip->ecc.strength = 4;
chip->ecc.read_page = flctl_read_page_hwecc;
chip->ecc.write_page = flctl_write_page_hwecc;
chip->ecc.mode = NAND_ECC_HW;
/* 4 symbols ECC enabled */
- writel(readl(FLCMNCR(flctl)) | _4ECCEN | ECCPOS2 | ECCPOS_02,
- FLCMNCR(flctl));
+ flctl->flcmncr_base |= _4ECCEN | ECCPOS2 | ECCPOS_02;
} else {
chip->ecc.mode = NAND_ECC_SOFT;
}
@@ -819,13 +863,13 @@ static int __devinit flctl_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get I/O memory\n");
- goto err;
+ goto err_iomap;
}
flctl->reg = ioremap(res->start, resource_size(res));
if (flctl->reg == NULL) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
- goto err;
+ goto err_iomap;
}
platform_set_drvdata(pdev, flctl);
@@ -833,9 +877,9 @@ static int __devinit flctl_probe(struct platform_device *pdev)
nand = &flctl->chip;
flctl_mtd->priv = nand;
flctl->pdev = pdev;
+ flctl->flcmncr_base = pdata->flcmncr_val;
flctl->hwecc = pdata->has_hwecc;
-
- flctl_register_init(flctl, pdata->flcmncr_val);
+ flctl->holden = pdata->use_holden;
nand->options = NAND_NO_AUTOINCR;
@@ -855,23 +899,28 @@ static int __devinit flctl_probe(struct platform_device *pdev)
nand->read_word = flctl_read_word;
}
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
+
ret = nand_scan_ident(flctl_mtd, 1, NULL);
if (ret)
- goto err;
+ goto err_chip;
ret = flctl_chip_init_tail(flctl_mtd);
if (ret)
- goto err;
+ goto err_chip;
ret = nand_scan_tail(flctl_mtd);
if (ret)
- goto err;
+ goto err_chip;
mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts);
return 0;
-err:
+err_chip:
+ pm_runtime_disable(&pdev->dev);
+err_iomap:
kfree(flctl);
return ret;
}
@@ -881,6 +930,7 @@ static int __devexit flctl_remove(struct platform_device *pdev)
struct sh_flctl *flctl = platform_get_drvdata(pdev);
nand_release(&flctl->mtd);
+ pm_runtime_disable(&pdev->dev);
kfree(flctl);
return 0;
diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c
index b175c0f..3421e37 100644
--- a/drivers/mtd/nand/sharpsl.c
+++ b/drivers/mtd/nand/sharpsl.c
@@ -167,6 +167,7 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev)
this->ecc.mode = NAND_ECC_HW;
this->ecc.size = 256;
this->ecc.bytes = 3;
+ this->ecc.strength = 1;
this->badblock_pattern = data->badblock_pattern;
this->ecc.layout = data->ecc_layout;
this->ecc.hwctl = sharpsl_nand_enable_hwecc;
@@ -181,8 +182,8 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev)
/* Register the partitions */
sharpsl->mtd.name = "sharpsl-nand";
- err = mtd_device_parse_register(&sharpsl->mtd, NULL, 0,
- data->partitions, data->nr_partitions);
+ err = mtd_device_parse_register(&sharpsl->mtd, NULL, NULL,
+ data->partitions, data->nr_partitions);
if (err)
goto err_add;
diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c
index 6caa0cd..5aa5180 100644
--- a/drivers/mtd/nand/tmio_nand.c
+++ b/drivers/mtd/nand/tmio_nand.c
@@ -430,6 +430,7 @@ static int tmio_probe(struct platform_device *dev)
nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = 512;
nand_chip->ecc.bytes = 6;
+ nand_chip->ecc.strength = 2;
nand_chip->ecc.hwctl = tmio_nand_enable_hwecc;
nand_chip->ecc.calculate = tmio_nand_calculate_ecc;
nand_chip->ecc.correct = tmio_nand_correct_data;
@@ -456,9 +457,9 @@ static int tmio_probe(struct platform_device *dev)
goto err_scan;
}
/* Register the partitions */
- retval = mtd_device_parse_register(mtd, NULL, 0,
- data ? data->partition : NULL,
- data ? data->num_partitions : 0);
+ retval = mtd_device_parse_register(mtd, NULL, NULL,
+ data ? data->partition : NULL,
+ data ? data->num_partitions : 0);
if (!retval)
return retval;
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c
index c7c4f1d..26398dc 100644
--- a/drivers/mtd/nand/txx9ndfmc.c
+++ b/drivers/mtd/nand/txx9ndfmc.c
@@ -356,6 +356,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
/* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */
chip->ecc.size = 256;
chip->ecc.bytes = 3;
+ chip->ecc.strength = 1;
chip->chip_delay = 100;
chip->controller = &drvdata->hw_control;
@@ -386,7 +387,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
}
mtd->name = txx9_priv->mtdname;
- mtd_device_parse_register(mtd, NULL, 0, NULL, 0);
+ mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
drvdata->mtds[i] = mtd;
}
diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c
index a75382a..c5f4ebf 100644
--- a/drivers/mtd/nftlcore.c
+++ b/drivers/mtd/nftlcore.c
@@ -56,13 +56,6 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
if (memcmp(mtd->name, "DiskOnChip", 10))
return;
- if (!mtd_can_have_bb(mtd)) {
- printk(KERN_ERR
-"NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
-"Please use the new diskonchip driver under the NAND subsystem.\n");
- return;
- }
-
pr_debug("NFTL: add_mtd for %s\n", mtd->name);
nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 772ad29..91467bb 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -1,6 +1,7 @@
menuconfig MTD_ONENAND
tristate "OneNAND Device Support"
depends on MTD
+ depends on HAS_IOMEM
help
This enables support for accessing all type of OneNAND flash
devices. For further information see
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c
index 0ccd5bf..1c4f97c 100644
--- a/drivers/mtd/onenand/generic.c
+++ b/drivers/mtd/onenand/generic.c
@@ -70,9 +70,9 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev)
goto out_iounmap;
}
- err = mtd_device_parse_register(&info->mtd, NULL, 0,
- pdata ? pdata->parts : NULL,
- pdata ? pdata->nr_parts : 0);
+ err = mtd_device_parse_register(&info->mtd, NULL, NULL,
+ pdata ? pdata->parts : NULL,
+ pdata ? pdata->nr_parts : 0);
platform_set_drvdata(pdev, info);
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index 7e9ea68..398a827 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -751,9 +751,9 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
if ((r = onenand_scan(&c->mtd, 1)) < 0)
goto err_release_regulator;
- r = mtd_device_parse_register(&c->mtd, NULL, 0,
- pdata ? pdata->parts : NULL,
- pdata ? pdata->nr_parts : 0);
+ r = mtd_device_parse_register(&c->mtd, NULL, NULL,
+ pdata ? pdata->parts : NULL,
+ pdata ? pdata->nr_parts : 0);
if (r)
goto err_release_onenand;
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index a061bc1..b3ce12e 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -1753,16 +1753,6 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
(int)len);
- /* Initialize retlen, in case of early exit */
- *retlen = 0;
-
- /* Do not allow writes past end of device */
- if (unlikely((to + len) > mtd->size)) {
- printk(KERN_ERR "%s: Attempt write to past end of device\n",
- __func__);
- return -EINVAL;
- }
-
/* Reject writes, which are not page aligned */
if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
printk(KERN_ERR "%s: Attempt to write not page aligned data\n",
@@ -1890,13 +1880,6 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
ops->retlen = 0;
ops->oobretlen = 0;
- /* Do not allow writes past end of device */
- if (unlikely((to + len) > mtd->size)) {
- printk(KERN_ERR "%s: Attempt write to past end of device\n",
- __func__);
- return -EINVAL;
- }
-
/* Reject writes, which are not page aligned */
if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
printk(KERN_ERR "%s: Attempt to write not page aligned data\n",
@@ -2493,12 +2476,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
(unsigned long long)instr->addr,
(unsigned long long)instr->len);
- /* Do not allow erase past end of device */
- if (unlikely((len + addr) > mtd->size)) {
- printk(KERN_ERR "%s: Erase past end of device\n", __func__);
- return -EINVAL;
- }
-
if (FLEXONENAND(this)) {
/* Find the eraseregion of this address */
int i = flexonenand_region(mtd, addr);
@@ -2525,8 +2502,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
return -EINVAL;
}
- instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
-
/* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_ERASING);
@@ -4103,33 +4078,34 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
mtd->oobavail = this->ecclayout->oobavail;
mtd->ecclayout = this->ecclayout;
+ mtd->ecc_strength = 1;
/* Fill in remaining MTD driver data */
mtd->type = ONENAND_IS_MLC(this) ? MTD_MLCNANDFLASH : MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
- mtd->erase = onenand_erase;
- mtd->point = NULL;
- mtd->unpoint = NULL;
- mtd->read = onenand_read;
- mtd->write = onenand_write;
- mtd->read_oob = onenand_read_oob;
- mtd->write_oob = onenand_write_oob;
- mtd->panic_write = onenand_panic_write;
+ mtd->_erase = onenand_erase;
+ mtd->_point = NULL;
+ mtd->_unpoint = NULL;
+ mtd->_read = onenand_read;
+ mtd->_write = onenand_write;
+ mtd->_read_oob = onenand_read_oob;
+ mtd->_write_oob = onenand_write_oob;
+ mtd->_panic_write = onenand_panic_write;
#ifdef CONFIG_MTD_ONENAND_OTP
- mtd->get_fact_prot_info = onenand_get_fact_prot_info;
- mtd->read_fact_prot_reg = onenand_read_fact_prot_reg;
- mtd->get_user_prot_info = onenand_get_user_prot_info;
- mtd->read_user_prot_reg = onenand_read_user_prot_reg;
- mtd->write_user_prot_reg = onenand_write_user_prot_reg;
- mtd->lock_user_prot_reg = onenand_lock_user_prot_reg;
+ mtd->_get_fact_prot_info = onenand_get_fact_prot_info;
+ mtd->_read_fact_prot_reg = onenand_read_fact_prot_reg;
+ mtd->_get_user_prot_info = onenand_get_user_prot_info;
+ mtd->_read_user_prot_reg = onenand_read_user_prot_reg;
+ mtd->_write_user_prot_reg = onenand_write_user_prot_reg;
+ mtd->_lock_user_prot_reg = onenand_lock_user_prot_reg;
#endif
- mtd->sync = onenand_sync;
- mtd->lock = onenand_lock;
- mtd->unlock = onenand_unlock;
- mtd->suspend = onenand_suspend;
- mtd->resume = onenand_resume;
- mtd->block_isbad = onenand_block_isbad;
- mtd->block_markbad = onenand_block_markbad;
+ mtd->_sync = onenand_sync;
+ mtd->_lock = onenand_lock;
+ mtd->_unlock = onenand_unlock;
+ mtd->_suspend = onenand_suspend;
+ mtd->_resume = onenand_resume;
+ mtd->_block_isbad = onenand_block_isbad;
+ mtd->_block_markbad = onenand_block_markbad;
mtd->owner = THIS_MODULE;
mtd->writebufsize = mtd->writesize;
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c
index fa1ee43..8e4b3f2 100644
--- a/drivers/mtd/onenand/samsung.c
+++ b/drivers/mtd/onenand/samsung.c
@@ -923,7 +923,7 @@ static int s3c_onenand_probe(struct platform_device *pdev)
r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!r) {
dev_err(&pdev->dev, "no buffer memory resource defined\n");
- return -ENOENT;
+ err = -ENOENT;
goto ahb_resource_failed;
}
@@ -964,7 +964,7 @@ static int s3c_onenand_probe(struct platform_device *pdev)
r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!r) {
dev_err(&pdev->dev, "no dma memory resource defined\n");
- return -ENOENT;
+ err = -ENOENT;
goto dma_resource_failed;
}
@@ -1014,7 +1014,7 @@ static int s3c_onenand_probe(struct platform_device *pdev)
if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ)
dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n");
- err = mtd_device_parse_register(mtd, NULL, 0,
+ err = mtd_device_parse_register(mtd, NULL, NULL,
pdata ? pdata->parts : NULL,
pdata ? pdata->nr_parts : 0);
diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c
index 48970c1..580035c 100644
--- a/drivers/mtd/redboot.c
+++ b/drivers/mtd/redboot.c
@@ -78,8 +78,7 @@ static int parse_redboot_partitions(struct mtd_info *master,
if ( directory < 0 ) {
offset = master->size + directory * master->erasesize;
- while (mtd_can_have_bb(master) &&
- mtd_block_isbad(master, offset)) {
+ while (mtd_block_isbad(master, offset)) {
if (!offset) {
nogood:
printk(KERN_NOTICE "Failed to find a non-bad block to check for RedBoot partition table\n");
@@ -89,8 +88,7 @@ static int parse_redboot_partitions(struct mtd_info *master,
}
} else {
offset = directory * master->erasesize;
- while (mtd_can_have_bb(master) &&
- mtd_block_isbad(master, offset)) {
+ while (mtd_block_isbad(master, offset)) {
offset += master->erasesize;
if (offset == master->size)
goto nogood;
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index 072ed59..9e2dfd5 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -1256,7 +1256,7 @@ static void sm_remove_dev(struct mtd_blktrans_dev *dev)
static struct mtd_blktrans_ops sm_ftl_ops = {
.name = "smblk",
- .major = -1,
+ .major = 0,
.part_bits = SM_FTL_PARTN_BITS,
.blksize = SM_SECTOR_SIZE,
.getgeo = sm_getgeo,
diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c
index 941bc3c..90b9882 100644
--- a/drivers/mtd/ubi/gluebi.c
+++ b/drivers/mtd/ubi/gluebi.c
@@ -174,11 +174,7 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len,
int err = 0, lnum, offs, total_read;
struct gluebi_device *gluebi;
- if (len < 0 || from < 0 || from + len > mtd->size)
- return -EINVAL;
-
gluebi = container_of(mtd, struct gluebi_device, mtd);
-
lnum = div_u64_rem(from, mtd->erasesize, &offs);
total_read = len;
while (total_read) {
@@ -218,14 +214,7 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len,
int err = 0, lnum, offs, total_written;
struct gluebi_device *gluebi;
- if (len < 0 || to < 0 || len + to > mtd->size)
- return -EINVAL;
-
gluebi = container_of(mtd, struct gluebi_device, mtd);
-
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
-
lnum = div_u64_rem(to, mtd->erasesize, &offs);
if (len % mtd->writesize || offs % mtd->writesize)
@@ -265,21 +254,13 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
int err, i, lnum, count;
struct gluebi_device *gluebi;
- if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize)
- return -EINVAL;
- if (instr->len < 0 || instr->addr + instr->len > mtd->size)
- return -EINVAL;
if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd))
return -EINVAL;
lnum = mtd_div_by_eb(instr->addr, mtd);
count = mtd_div_by_eb(instr->len, mtd);
-
gluebi = container_of(mtd, struct gluebi_device, mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
-
for (i = 0; i < count - 1; i++) {
err = ubi_leb_unmap(gluebi->desc, lnum + i);
if (err)
@@ -340,11 +321,11 @@ static int gluebi_create(struct ubi_device_info *di,
mtd->owner = THIS_MODULE;
mtd->writesize = di->min_io_size;
mtd->erasesize = vi->usable_leb_size;
- mtd->read = gluebi_read;
- mtd->write = gluebi_write;
- mtd->erase = gluebi_erase;
- mtd->get_device = gluebi_get_device;
- mtd->put_device = gluebi_put_device;
+ mtd->_read = gluebi_read;
+ mtd->_write = gluebi_write;
+ mtd->_erase = gluebi_erase;
+ mtd->_get_device = gluebi_get_device;
+ mtd->_put_device = gluebi_put_device;
/*
* In case of dynamic a volume, MTD device size is just volume size. In
diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c
index 9abd4eb..dd5e048 100644
--- a/drivers/net/appletalk/cops.c
+++ b/drivers/net/appletalk/cops.c
@@ -70,7 +70,6 @@ static const char *version =
#include <linux/bitops.h>
#include <linux/jiffies.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c
index 6057b30..0910dce 100644
--- a/drivers/net/appletalk/ltpc.c
+++ b/drivers/net/appletalk/ltpc.c
@@ -229,7 +229,6 @@ static int dma;
#include <linux/bitops.h>
#include <linux/gfp.h>
-#include <asm/system.h>
#include <asm/dma.h>
#include <asm/io.h>
diff --git a/drivers/net/arcnet/com20020_cs.c b/drivers/net/arcnet/com20020_cs.c
index 980e65c..5bed4c4 100644
--- a/drivers/net/arcnet/com20020_cs.c
+++ b/drivers/net/arcnet/com20020_cs.c
@@ -47,7 +47,6 @@
#include <pcmcia/ds.h>
#include <asm/io.h>
-#include <asm/system.h>
#define VERSION "arcnet: COM20020 PCMCIA support loaded.\n"
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index a20b585..941b4e1 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -54,7 +54,6 @@
#include <linux/inet.h>
#include <linux/bitops.h>
#include <linux/io.h>
-#include <asm/system.h>
#include <asm/dma.h>
#include <linux/uaccess.h>
#include <linux/errno.h>
diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c
index 98a5a7d..034c16b 100644
--- a/drivers/net/can/slcan.c
+++ b/drivers/net/can/slcan.c
@@ -40,7 +40,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/string.h>
diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c
index 7cb2785..ec03b40 100644
--- a/drivers/net/cris/eth_v10.c
+++ b/drivers/net/cris/eth_v10.c
@@ -35,7 +35,6 @@
#include <asm/io.h> /* CRIS_LED_* I/O functions */
#include <asm/irq.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <asm/ethernet.h>
#include <asm/cache.h>
#include <arch/io_interface_mux.h>
diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c
index e61b2f8..66df936 100644
--- a/drivers/net/ethernet/3com/3c574_cs.c
+++ b/drivers/net/ethernet/3com/3c574_cs.c
@@ -95,7 +95,6 @@ earlier 3Com products.
#include <asm/uaccess.h>
#include <asm/io.h>
-#include <asm/system.h>
/*====================================================================*/
diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c
index b23253b..a556c01 100644
--- a/drivers/net/ethernet/3com/3c589_cs.c
+++ b/drivers/net/ethernet/3com/3c589_cs.c
@@ -50,7 +50,6 @@
#include <asm/uaccess.h>
#include <asm/io.h>
-#include <asm/system.h>
/* To minimize the size of the driver source I only define operating
constants if they are used several times. You'll need the manual
diff --git a/drivers/net/ethernet/8390/3c503.c b/drivers/net/ethernet/8390/3c503.c
index fbab136..49d76bd 100644
--- a/drivers/net/ethernet/8390/3c503.c
+++ b/drivers/net/ethernet/8390/3c503.c
@@ -54,7 +54,6 @@ static const char version[] =
#include <asm/uaccess.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
#include "8390.h"
diff --git a/drivers/net/ethernet/8390/ac3200.c b/drivers/net/ethernet/8390/ac3200.c
index 5337dd0..ccf0794 100644
--- a/drivers/net/ethernet/8390/ac3200.c
+++ b/drivers/net/ethernet/8390/ac3200.c
@@ -34,7 +34,6 @@ static const char version[] =
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
diff --git a/drivers/net/ethernet/8390/apne.c b/drivers/net/ethernet/8390/apne.c
index 3ad5d2f..9239592 100644
--- a/drivers/net/ethernet/8390/apne.c
+++ b/drivers/net/ethernet/8390/apne.c
@@ -39,7 +39,6 @@
#include <linux/interrupt.h>
#include <linux/jiffies.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <asm/amigaints.h>
diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c
index c30adcc9..11476ca 100644
--- a/drivers/net/ethernet/8390/ax88796.c
+++ b/drivers/net/ethernet/8390/ax88796.c
@@ -31,7 +31,6 @@
#include <net/ax88796.h>
-#include <asm/system.h>
/* Rename the lib8390.c functions to show that they are in this driver */
#define __ei_open ax_ei_open
diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c
index c5bd8eb..e1b3941 100644
--- a/drivers/net/ethernet/8390/axnet_cs.c
+++ b/drivers/net/ethernet/8390/axnet_cs.c
@@ -46,7 +46,6 @@
#include <pcmcia/cisreg.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/ethernet/8390/e2100.c b/drivers/net/ethernet/8390/e2100.c
index d16dc53..ed55ce8 100644
--- a/drivers/net/ethernet/8390/e2100.c
+++ b/drivers/net/ethernet/8390/e2100.c
@@ -48,7 +48,6 @@ static const char version[] =
#include <linux/delay.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "8390.h"
diff --git a/drivers/net/ethernet/8390/es3210.c b/drivers/net/ethernet/8390/es3210.c
index 6428f9e..ba1b5c9 100644
--- a/drivers/net/ethernet/8390/es3210.c
+++ b/drivers/net/ethernet/8390/es3210.c
@@ -59,7 +59,6 @@ static const char version[] =
#include <linux/etherdevice.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "8390.h"
diff --git a/drivers/net/ethernet/8390/etherh.c b/drivers/net/ethernet/8390/etherh.c
index 48c4948..dbefd56 100644
--- a/drivers/net/ethernet/8390/etherh.c
+++ b/drivers/net/ethernet/8390/etherh.c
@@ -45,9 +45,9 @@
#include <linux/bitops.h>
#include <linux/jiffies.h>
-#include <asm/system.h>
#include <asm/ecard.h>
#include <asm/io.h>
+#include <asm/system_info.h>
#define EI_SHIFT(x) (ei_local->reg_offset[x])
diff --git a/drivers/net/ethernet/8390/hp-plus.c b/drivers/net/ethernet/8390/hp-plus.c
index d42938b..52f70f9 100644
--- a/drivers/net/ethernet/8390/hp-plus.c
+++ b/drivers/net/ethernet/8390/hp-plus.c
@@ -33,7 +33,6 @@ static const char version[] =
#include <linux/interrupt.h>
#include <linux/delay.h>
-#include <asm/system.h>
#include <asm/io.h>
#include "8390.h"
diff --git a/drivers/net/ethernet/8390/hp.c b/drivers/net/ethernet/8390/hp.c
index 113f1e0..37fa89a 100644
--- a/drivers/net/ethernet/8390/hp.c
+++ b/drivers/net/ethernet/8390/hp.c
@@ -33,7 +33,6 @@ static const char version[] =
#include <linux/interrupt.h>
#include <linux/delay.h>
-#include <asm/system.h>
#include <asm/io.h>
#include "8390.h"
diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c
index e77f624..b329f5c 100644
--- a/drivers/net/ethernet/8390/lib8390.c
+++ b/drivers/net/ethernet/8390/lib8390.c
@@ -57,7 +57,6 @@
#include <linux/types.h>
#include <linux/string.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <asm/irq.h>
diff --git a/drivers/net/ethernet/8390/lne390.c b/drivers/net/ethernet/8390/lne390.c
index 69490ae..479409b 100644
--- a/drivers/net/ethernet/8390/lne390.c
+++ b/drivers/net/ethernet/8390/lne390.c
@@ -46,7 +46,6 @@ static const char *version =
#include <linux/etherdevice.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "8390.h"
diff --git a/drivers/net/ethernet/8390/mac8390.c b/drivers/net/ethernet/8390/mac8390.c
index af5d982..88ccc8b 100644
--- a/drivers/net/ethernet/8390/mac8390.c
+++ b/drivers/net/ethernet/8390/mac8390.c
@@ -37,7 +37,6 @@
#include <linux/bitops.h>
#include <linux/io.h>
-#include <asm/system.h>
#include <asm/dma.h>
#include <asm/hwtest.h>
#include <asm/macints.h>
diff --git a/drivers/net/ethernet/8390/ne-h8300.c b/drivers/net/ethernet/8390/ne-h8300.c
index 9b9c77d..7fc28f2 100644
--- a/drivers/net/ethernet/8390/ne-h8300.c
+++ b/drivers/net/ethernet/8390/ne-h8300.c
@@ -29,7 +29,6 @@ static const char version1[] =
#include <linux/etherdevice.h>
#include <linux/jiffies.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
diff --git a/drivers/net/ethernet/8390/ne.c b/drivers/net/ethernet/8390/ne.c
index f92ea2a..d04911d 100644
--- a/drivers/net/ethernet/8390/ne.c
+++ b/drivers/net/ethernet/8390/ne.c
@@ -53,7 +53,6 @@ static const char version2[] =
#include <linux/jiffies.h>
#include <linux/platform_device.h>
-#include <asm/system.h>
#include <asm/io.h>
#include "8390.h"
diff --git a/drivers/net/ethernet/8390/ne2.c b/drivers/net/ethernet/8390/ne2.c
index 922b320..ef85839 100644
--- a/drivers/net/ethernet/8390/ne2.c
+++ b/drivers/net/ethernet/8390/ne2.c
@@ -76,7 +76,6 @@ static const char *version = "ne2.c:v0.91 Nov 16 1998 Wim Dumon <wimpie@kotnet.o
#include <linux/bitops.h>
#include <linux/jiffies.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
diff --git a/drivers/net/ethernet/8390/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c
index 3fab04a..5e8845f 100644
--- a/drivers/net/ethernet/8390/ne2k-pci.c
+++ b/drivers/net/ethernet/8390/ne2k-pci.c
@@ -54,7 +54,6 @@ static int options[MAX_UNITS];
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/ethernet/8390/ne3210.c b/drivers/net/ethernet/8390/ne3210.c
index 2a3e805..a2f8b2b 100644
--- a/drivers/net/ethernet/8390/ne3210.c
+++ b/drivers/net/ethernet/8390/ne3210.c
@@ -39,7 +39,6 @@
#include <linux/mm.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "8390.h"
diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c
index f2a4e5d..de1af0b 100644
--- a/drivers/net/ethernet/8390/pcnet_cs.c
+++ b/drivers/net/ethernet/8390/pcnet_cs.c
@@ -49,7 +49,6 @@
#include <pcmcia/cisreg.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/ethernet/8390/smc-mca.c b/drivers/net/ethernet/8390/smc-mca.c
index 77efec4..7a68590 100644
--- a/drivers/net/ethernet/8390/smc-mca.c
+++ b/drivers/net/ethernet/8390/smc-mca.c
@@ -47,7 +47,6 @@
#include <linux/etherdevice.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "8390.h"
diff --git a/drivers/net/ethernet/8390/smc-ultra.c b/drivers/net/ethernet/8390/smc-ultra.c
index 1cc306a..b0fbce3 100644
--- a/drivers/net/ethernet/8390/smc-ultra.c
+++ b/drivers/net/ethernet/8390/smc-ultra.c
@@ -69,7 +69,6 @@ static const char version[] =
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include "8390.h"
diff --git a/drivers/net/ethernet/8390/smc-ultra32.c b/drivers/net/ethernet/8390/smc-ultra32.c
index bb87053..923e42a 100644
--- a/drivers/net/ethernet/8390/smc-ultra32.c
+++ b/drivers/net/ethernet/8390/smc-ultra32.c
@@ -57,7 +57,6 @@ static const char *version = "smc-ultra32.c: 06/97 v1.00\n";
#include <linux/etherdevice.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "8390.h"
diff --git a/drivers/net/ethernet/8390/stnic.c b/drivers/net/ethernet/8390/stnic.c
index 3b90375..8df4c41 100644
--- a/drivers/net/ethernet/8390/stnic.c
+++ b/drivers/net/ethernet/8390/stnic.c
@@ -17,7 +17,6 @@
#include <linux/init.h>
#include <linux/delay.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <mach-se/mach/se.h>
#include <asm/machvec.h>
diff --git a/drivers/net/ethernet/8390/wd.c b/drivers/net/ethernet/8390/wd.c
index c175fad..03eb3ee 100644
--- a/drivers/net/ethernet/8390/wd.c
+++ b/drivers/net/ethernet/8390/wd.c
@@ -39,7 +39,6 @@ static const char version[] =
#include <linux/etherdevice.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "8390.h"
diff --git a/drivers/net/ethernet/8390/zorro8390.c b/drivers/net/ethernet/8390/zorro8390.c
index bcd2732..7818e63 100644
--- a/drivers/net/ethernet/8390/zorro8390.c
+++ b/drivers/net/ethernet/8390/zorro8390.c
@@ -31,7 +31,6 @@
#include <linux/zorro.h>
#include <linux/jiffies.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
diff --git a/drivers/net/ethernet/alteon/acenic.c b/drivers/net/ethernet/alteon/acenic.c
index 6c3b1c0..7219123 100644
--- a/drivers/net/ethernet/alteon/acenic.c
+++ b/drivers/net/ethernet/alteon/acenic.c
@@ -78,7 +78,6 @@
#include <net/sock.h>
#include <net/ip.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
diff --git a/drivers/net/ethernet/amd/7990.c b/drivers/net/ethernet/amd/7990.c
index 1b046f5..6e722dc 100644
--- a/drivers/net/ethernet/amd/7990.c
+++ b/drivers/net/ethernet/amd/7990.c
@@ -33,7 +33,6 @@
#include <linux/socket.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/pgtable.h>
diff --git a/drivers/net/ethernet/amd/am79c961a.c b/drivers/net/ethernet/amd/am79c961a.c
index cc7b9e4..e10ffad 100644
--- a/drivers/net/ethernet/amd/am79c961a.c
+++ b/drivers/net/ethernet/amd/am79c961a.c
@@ -30,7 +30,6 @@
#include <linux/io.h>
#include <mach/hardware.h>
-#include <asm/system.h>
#define TX_BUFFERS 15
#define RX_BUFFERS 25
diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c
index 9f62504..64d0d9c 100644
--- a/drivers/net/ethernet/amd/amd8111e.c
+++ b/drivers/net/ethernet/amd/amd8111e.c
@@ -88,7 +88,6 @@ Revision History:
#include <linux/crc32.h>
#include <linux/dma-mapping.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/ethernet/amd/declance.c b/drivers/net/ethernet/amd/declance.c
index 7dc508e..75299f5 100644
--- a/drivers/net/ethernet/amd/declance.c
+++ b/drivers/net/ethernet/amd/declance.c
@@ -64,7 +64,6 @@
#include <linux/types.h>
#include <asm/addrspace.h>
-#include <asm/system.h>
#include <asm/dec/interrupts.h>
#include <asm/dec/ioasic.h>
diff --git a/drivers/net/ethernet/amd/hplance.c b/drivers/net/ethernet/amd/hplance.c
index 4e2d68a..8baff4e 100644
--- a/drivers/net/ethernet/amd/hplance.c
+++ b/drivers/net/ethernet/amd/hplance.c
@@ -22,7 +22,6 @@
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/pgtable.h>
diff --git a/drivers/net/ethernet/amd/mvme147.c b/drivers/net/ethernet/amd/mvme147.c
index 56bc47a9..9af3c30 100644
--- a/drivers/net/ethernet/amd/mvme147.c
+++ b/drivers/net/ethernet/amd/mvme147.c
@@ -22,7 +22,6 @@
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/mvme147hw.h>
diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c
index ebdb9e2..9f59bf6 100644
--- a/drivers/net/ethernet/amd/nmclan_cs.c
+++ b/drivers/net/ethernet/amd/nmclan_cs.c
@@ -154,7 +154,6 @@ Include Files
#include <asm/uaccess.h>
#include <asm/io.h>
-#include <asm/system.h>
/* ----------------------------------------------------------------------------
Defines
diff --git a/drivers/net/ethernet/amd/sunlance.c b/drivers/net/ethernet/amd/sunlance.c
index e3fe350..d7a3533 100644
--- a/drivers/net/ethernet/amd/sunlance.c
+++ b/drivers/net/ethernet/amd/sunlance.c
@@ -95,7 +95,6 @@ static char lancestr[] = "LANCE";
#include <linux/of_device.h>
#include <linux/gfp.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/pgtable.h>
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index d2ff8ee..062ac33 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -48,7 +48,6 @@
#include <net/checksum.h>
#include <net/ip.h>
-#include <asm/system.h>
#include <linux/io.h>
#include <asm/byteorder.h>
#include <linux/uaccess.h>
diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c
index 30fee42..b9406cb 100644
--- a/drivers/net/ethernet/cirrus/cs89x0.c
+++ b/drivers/net/ethernet/cirrus/cs89x0.c
@@ -148,7 +148,6 @@
#include <linux/delay.h>
#include <linux/gfp.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/atomic.h>
diff --git a/drivers/net/ethernet/cirrus/mac89x0.c b/drivers/net/ethernet/cirrus/mac89x0.c
index 932fdcc..e285f38 100644
--- a/drivers/net/ethernet/cirrus/mac89x0.c
+++ b/drivers/net/ethernet/cirrus/mac89x0.c
@@ -99,7 +99,6 @@ static char *version =
#include <linux/bitops.h>
#include <linux/gfp.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/hwtest.h>
#include <asm/macints.h>
diff --git a/drivers/net/ethernet/dlink/de600.c b/drivers/net/ethernet/dlink/de600.c
index 682750c..414f0ee 100644
--- a/drivers/net/ethernet/dlink/de600.c
+++ b/drivers/net/ethernet/dlink/de600.c
@@ -46,7 +46,6 @@ static const char version[] = "de600.c: $Revision: 1.41-2.5 $, Bjorn Ekwall (bj
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
-#include <asm/system.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/delay.h>
diff --git a/drivers/net/ethernet/dlink/de620.c b/drivers/net/ethernet/dlink/de620.c
index afc5aaa..2e2bc60 100644
--- a/drivers/net/ethernet/dlink/de620.c
+++ b/drivers/net/ethernet/dlink/de620.c
@@ -122,7 +122,6 @@ static const char version[] =
#include <linux/skbuff.h>
#include <asm/io.h>
-#include <asm/system.h>
/* Constant definitions for the DE-620 registers, commands and bits */
#include "de620.h"
diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c
index 4e3cd2f..17a46e7 100644
--- a/drivers/net/ethernet/freescale/ucc_geth.c
+++ b/drivers/net/ethernet/freescale/ucc_geth.c
@@ -3945,6 +3945,8 @@ static int ucc_geth_probe(struct platform_device* ofdev)
}
if (max_speed == SPEED_1000) {
+ unsigned int snums = qe_get_num_of_snums();
+
/* configure muram FIFOs for gigabit operation */
ug_info->uf_info.urfs = UCC_GETH_URFS_GIGA_INIT;
ug_info->uf_info.urfet = UCC_GETH_URFET_GIGA_INIT;
@@ -3954,11 +3956,11 @@ static int ucc_geth_probe(struct platform_device* ofdev)
ug_info->uf_info.utftt = UCC_GETH_UTFTT_GIGA_INIT;
ug_info->numThreadsTx = UCC_GETH_NUM_OF_THREADS_4;
- /* If QE's snum number is 46 which means we need to support
+ /* If QE's snum number is 46/76 which means we need to support
* 4 UECs at 1000Base-T simultaneously, we need to allocate
* more Threads to Rx.
*/
- if (qe_get_num_of_snums() == 46)
+ if ((snums == 76) || (snums == 46))
ug_info->numThreadsRx = UCC_GETH_NUM_OF_THREADS_6;
else
ug_info->numThreadsRx = UCC_GETH_NUM_OF_THREADS_4;
diff --git a/drivers/net/ethernet/fujitsu/at1700.c b/drivers/net/ethernet/fujitsu/at1700.c
index 586b46f..3d94797 100644
--- a/drivers/net/ethernet/fujitsu/at1700.c
+++ b/drivers/net/ethernet/fujitsu/at1700.c
@@ -52,7 +52,6 @@
#include <linux/crc32.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
diff --git a/drivers/net/ethernet/fujitsu/eth16i.c b/drivers/net/ethernet/fujitsu/eth16i.c
index c3f0178..a992d1f 100644
--- a/drivers/net/ethernet/fujitsu/eth16i.c
+++ b/drivers/net/ethernet/fujitsu/eth16i.c
@@ -163,7 +163,6 @@ static char *version =
#include <linux/jiffies.h>
#include <linux/io.h>
-#include <asm/system.h>
#include <asm/dma.h>
diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c
index 0230319..2418faf 100644
--- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c
+++ b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c
@@ -57,7 +57,6 @@
#include <asm/uaccess.h>
#include <asm/io.h>
-#include <asm/system.h>
/*====================================================================*/
diff --git a/drivers/net/ethernet/i825xx/3c507.c b/drivers/net/ethernet/i825xx/3c507.c
index ed6925f..e8984b05 100644
--- a/drivers/net/ethernet/i825xx/3c507.c
+++ b/drivers/net/ethernet/i825xx/3c507.c
@@ -63,7 +63,6 @@ static const char version[] =
#include <asm/dma.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
/* use 0 for production, 1 for verification, 2..7 for debug */
diff --git a/drivers/net/ethernet/i825xx/3c527.c b/drivers/net/ethernet/i825xx/3c527.c
index ef43f3e..278e791 100644
--- a/drivers/net/ethernet/i825xx/3c527.c
+++ b/drivers/net/ethernet/i825xx/3c527.c
@@ -106,7 +106,6 @@ DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Richard Procter <rnp@paradise.net.
#include <linux/semaphore.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
diff --git a/drivers/net/ethernet/i825xx/eepro.c b/drivers/net/ethernet/i825xx/eepro.c
index 7a4ad4a..7f49fd5 100644
--- a/drivers/net/ethernet/i825xx/eepro.c
+++ b/drivers/net/ethernet/i825xx/eepro.c
@@ -148,7 +148,6 @@ static const char version[] =
#include <linux/bitops.h>
#include <linux/ethtool.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
diff --git a/drivers/net/ethernet/i825xx/eexpress.c b/drivers/net/ethernet/i825xx/eexpress.c
index 3fc649e..cc2e66a 100644
--- a/drivers/net/ethernet/i825xx/eexpress.c
+++ b/drivers/net/ethernet/i825xx/eexpress.c
@@ -116,7 +116,6 @@
#include <linux/bitops.h>
#include <linux/jiffies.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
diff --git a/drivers/net/ethernet/i825xx/ether1.c b/drivers/net/ethernet/i825xx/ether1.c
index 406a12b..067db3f 100644
--- a/drivers/net/ethernet/i825xx/ether1.c
+++ b/drivers/net/ethernet/i825xx/ether1.c
@@ -48,7 +48,6 @@
#include <linux/skbuff.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/ecard.h>
diff --git a/drivers/net/ethernet/i825xx/znet.c b/drivers/net/ethernet/i825xx/znet.c
index a436497..bd1f1ef 100644
--- a/drivers/net/ethernet/i825xx/znet.c
+++ b/drivers/net/ethernet/i825xx/znet.c
@@ -100,7 +100,6 @@
#include <linux/if_arp.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index e877371..9010cea 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -1616,11 +1616,8 @@ static struct vio_driver ibmveth_driver = {
.probe = ibmveth_probe,
.remove = ibmveth_remove,
.get_desired_dma = ibmveth_get_desired_dma,
- .driver = {
- .name = ibmveth_driver_name,
- .owner = THIS_MODULE,
- .pm = &ibmveth_pm_ops,
- }
+ .name = ibmveth_driver_name,
+ .pm = &ibmveth_pm_ops,
};
static int __init ibmveth_module_init(void)
diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c
index f30db1c..bc58f1d 100644
--- a/drivers/net/ethernet/korina.c
+++ b/drivers/net/ethernet/korina.c
@@ -55,7 +55,6 @@
#include <linux/crc32.h>
#include <asm/bootinfo.h>
-#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/pgtable.h>
#include <asm/io.h>
diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
index 75af1af..5e1ca0f 100644
--- a/drivers/net/ethernet/marvell/mv643xx_eth.c
+++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
@@ -57,7 +57,6 @@
#include <linux/types.h>
#include <linux/inet_lro.h>
#include <linux/slab.h>
-#include <asm/system.h>
static char mv643xx_eth_driver_name[] = "mv643xx_eth";
static char mv643xx_eth_driver_version[] = "1.4";
diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c
index 45a6333..efec6b6 100644
--- a/drivers/net/ethernet/marvell/pxa168_eth.c
+++ b/drivers/net/ethernet/marvell/pxa168_eth.c
@@ -43,7 +43,6 @@
#include <linux/interrupt.h>
#include <linux/types.h>
#include <asm/pgtable.h>
-#include <asm/system.h>
#include <asm/cacheflush.h>
#include <linux/pxa168_eth.h>
diff --git a/drivers/net/ethernet/micrel/ks8842.c b/drivers/net/ethernet/micrel/ks8842.c
index 0686b93..f84dd2d 100644
--- a/drivers/net/ethernet/micrel/ks8842.c
+++ b/drivers/net/ethernet/micrel/ks8842.c
@@ -458,7 +458,7 @@ static int ks8842_tx_frame_dma(struct sk_buff *skb, struct net_device *netdev)
if (sg_dma_len(&ctl->sg) % 4)
sg_dma_len(&ctl->sg) += 4 - sg_dma_len(&ctl->sg) % 4;
- ctl->adesc = ctl->chan->device->device_prep_slave_sg(ctl->chan,
+ ctl->adesc = dmaengine_prep_slave_sg(ctl->chan,
&ctl->sg, 1, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
if (!ctl->adesc)
@@ -570,7 +570,7 @@ static int __ks8842_start_new_rx_dma(struct net_device *netdev)
sg_dma_len(sg) = DMA_BUFFER_SIZE;
- ctl->adesc = ctl->chan->device->device_prep_slave_sg(ctl->chan,
+ ctl->adesc = dmaengine_prep_slave_sg(ctl->chan,
sg, 1, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
diff --git a/drivers/net/ethernet/natsemi/jazzsonic.c b/drivers/net/ethernet/natsemi/jazzsonic.c
index 5b89fd3..95dd39f 100644
--- a/drivers/net/ethernet/natsemi/jazzsonic.c
+++ b/drivers/net/ethernet/natsemi/jazzsonic.c
@@ -38,7 +38,6 @@
#include <linux/slab.h>
#include <asm/bootinfo.h>
-#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/dma.h>
diff --git a/drivers/net/ethernet/natsemi/macsonic.c b/drivers/net/ethernet/natsemi/macsonic.c
index e640e23..b9680ba 100644
--- a/drivers/net/ethernet/natsemi/macsonic.c
+++ b/drivers/net/ethernet/natsemi/macsonic.c
@@ -53,7 +53,6 @@
#include <linux/slab.h>
#include <asm/bootinfo.h>
-#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/hwtest.h>
diff --git a/drivers/net/ethernet/natsemi/ns83820.c b/drivers/net/ethernet/natsemi/ns83820.c
index c24b46c..d52728b 100644
--- a/drivers/net/ethernet/natsemi/ns83820.c
+++ b/drivers/net/ethernet/natsemi/ns83820.c
@@ -121,7 +121,6 @@
#include <asm/io.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#define DRV_NAME "ns83820"
diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c
index 22a8de0..6338ef8 100644
--- a/drivers/net/ethernet/neterion/s2io.c
+++ b/drivers/net/ethernet/neterion/s2io.c
@@ -81,7 +81,6 @@
#include <linux/prefetch.h>
#include <net/tcp.h>
-#include <asm/system.h>
#include <asm/div64.h>
#include <asm/irq.h>
diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
index 8561dd2..aca1304 100644
--- a/drivers/net/ethernet/nvidia/forcedeth.c
+++ b/drivers/net/ethernet/nvidia/forcedeth.c
@@ -69,7 +69,6 @@
#include <linux/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#define TX_WORK_PER_LOOP 64
#define RX_WORK_PER_LOOP 64
diff --git a/drivers/net/ethernet/realtek/atp.c b/drivers/net/ethernet/realtek/atp.c
index 46c1932..e02f04d 100644
--- a/drivers/net/ethernet/realtek/atp.c
+++ b/drivers/net/ethernet/realtek/atp.c
@@ -140,7 +140,6 @@ static int xcvr[NUM_UNITS]; /* The data transfer mode. */
#include <linux/delay.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 27c358c..7b23554 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -29,7 +29,6 @@
#include <linux/pci-aspm.h>
#include <linux/prefetch.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
diff --git a/drivers/net/ethernet/seeq/ether3.c b/drivers/net/ethernet/seeq/ether3.c
index 7b819bd..df808ac 100644
--- a/drivers/net/ethernet/seeq/ether3.c
+++ b/drivers/net/ethernet/seeq/ether3.c
@@ -64,7 +64,6 @@
#include <linux/delay.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/ecard.h>
#include <asm/io.h>
diff --git a/drivers/net/ethernet/seeq/seeq8005.c b/drivers/net/ethernet/seeq/seeq8005.c
index 7989907..698edbb 100644
--- a/drivers/net/ethernet/seeq/seeq8005.c
+++ b/drivers/net/ethernet/seeq/seeq8005.c
@@ -47,7 +47,6 @@ static const char version[] =
#include <linux/bitops.h>
#include <linux/jiffies.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
diff --git a/drivers/net/ethernet/sfc/mtd.c b/drivers/net/ethernet/sfc/mtd.c
index 26b3c23..7581483 100644
--- a/drivers/net/ethernet/sfc/mtd.c
+++ b/drivers/net/ethernet/sfc/mtd.c
@@ -193,7 +193,7 @@ static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
erase->state = MTD_ERASE_DONE;
} else {
erase->state = MTD_ERASE_FAILED;
- erase->fail_addr = 0xffffffff;
+ erase->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
}
mtd_erase_callback(erase);
return rc;
@@ -263,10 +263,10 @@ static int efx_mtd_probe_device(struct efx_nic *efx, struct efx_mtd *efx_mtd)
part->mtd.owner = THIS_MODULE;
part->mtd.priv = efx_mtd;
part->mtd.name = part->name;
- part->mtd.erase = efx_mtd_erase;
- part->mtd.read = efx_mtd->ops->read;
- part->mtd.write = efx_mtd->ops->write;
- part->mtd.sync = efx_mtd_sync;
+ part->mtd._erase = efx_mtd_erase;
+ part->mtd._read = efx_mtd->ops->read;
+ part->mtd._write = efx_mtd->ops->write;
+ part->mtd._sync = efx_mtd_sync;
if (mtd_device_register(&part->mtd, NULL, 0))
goto fail;
diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c
index d12e48a..04393b5 100644
--- a/drivers/net/ethernet/smsc/smc91c92_cs.c
+++ b/drivers/net/ethernet/smsc/smc91c92_cs.c
@@ -53,7 +53,6 @@
#include <pcmcia/ss.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
/*====================================================================*/
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
index 1dc4fad..fee4493 100644
--- a/drivers/net/ethernet/smsc/smc91x.c
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -2280,7 +2280,7 @@ static int __devinit smc_drv_probe(struct platform_device *pdev)
if (ret)
goto out_release_io;
#if defined(CONFIG_SA1100_ASSABET)
- NCR_0 |= NCR_ENET_OSC_EN;
+ neponset_ncr_set(NCR_ENET_OSC_EN);
#endif
platform_set_drvdata(pdev, ndev);
ret = smc_enable_device(pdev);
diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c
index 3c22955..ce4df61 100644
--- a/drivers/net/ethernet/sun/cassini.c
+++ b/drivers/net/ethernet/sun/cassini.c
@@ -99,7 +99,6 @@
#include <net/checksum.h>
#include <linux/atomic.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/ethernet/sun/sunbmac.c b/drivers/net/ethernet/sun/sunbmac.c
index f359863..2a83fc5 100644
--- a/drivers/net/ethernet/sun/sunbmac.c
+++ b/drivers/net/ethernet/sun/sunbmac.c
@@ -35,7 +35,6 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/pgtable.h>
-#include <asm/system.h>
#include "sunbmac.h"
diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c
index ba04159..558409f 100644
--- a/drivers/net/ethernet/sun/sungem.c
+++ b/drivers/net/ethernet/sun/sungem.c
@@ -41,7 +41,6 @@
#include <linux/mm.h>
#include <linux/gfp.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/ethernet/sun/sunhme.c b/drivers/net/ethernet/sun/sunhme.c
index 8b627e2..b95e7e6 100644
--- a/drivers/net/ethernet/sun/sunhme.c
+++ b/drivers/net/ethernet/sun/sunhme.c
@@ -36,7 +36,6 @@
#include <linux/bitops.h>
#include <linux/dma-mapping.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/byteorder.h>
diff --git a/drivers/net/ethernet/sun/sunqe.c b/drivers/net/ethernet/sun/sunqe.c
index 139d6b4..7d4a040 100644
--- a/drivers/net/ethernet/sun/sunqe.c
+++ b/drivers/net/ethernet/sun/sunqe.c
@@ -28,7 +28,6 @@
#include <linux/of.h>
#include <linux/of_device.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/byteorder.h>
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index 92a037a..38e3ae9 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -1259,10 +1259,7 @@ static struct vio_driver vnet_port_driver = {
.id_table = vnet_port_match,
.probe = vnet_port_probe,
.remove = vnet_port_remove,
- .driver = {
- .name = "vnet_port",
- .owner = THIS_MODULE,
- }
+ .name = "vnet_port",
};
static int __init vnet_init(void)
diff --git a/drivers/net/ethernet/tundra/tsi108_eth.c b/drivers/net/ethernet/tundra/tsi108_eth.c
index 840e0e9..277c93e 100644
--- a/drivers/net/ethernet/tundra/tsi108_eth.c
+++ b/drivers/net/ethernet/tundra/tsi108_eth.c
@@ -50,7 +50,6 @@
#include <linux/platform_device.h>
#include <linux/gfp.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/tsi108.h>
diff --git a/drivers/net/ethernet/xircom/xirc2ps_cs.c b/drivers/net/ethernet/xircom/xirc2ps_cs.c
index 5c69c6f..94a1f94 100644
--- a/drivers/net/ethernet/xircom/xirc2ps_cs.c
+++ b/drivers/net/ethernet/xircom/xirc2ps_cs.c
@@ -89,7 +89,6 @@
#include <pcmcia/ciscode.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#ifndef MANFID_COMPAQ
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index 2a5a34d..64783a0 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -13,7 +13,6 @@
*/
#include <linux/module.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/bitops.h>
#include <linux/string.h>
diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c
index f1aea0c..acb6369 100644
--- a/drivers/net/hamradio/baycom_par.c
+++ b/drivers/net/hamradio/baycom_par.c
@@ -86,7 +86,6 @@
#include <linux/bitops.h>
#include <linux/jiffies.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index 18d8aff..76d5477 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -69,7 +69,6 @@
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c
index bc02968..aed1a61 100644
--- a/drivers/net/hamradio/mkiss.c
+++ b/drivers/net/hamradio/mkiss.c
@@ -17,7 +17,6 @@
* Copyright (C) 2004, 05 Thomas Osterried DL9SAU <thomas@x-berg.in-berlin.de>
*/
#include <linux/module.h>
-#include <asm/system.h>
#include <linux/bitops.h>
#include <asm/uaccess.h>
#include <linux/crc16.h>
diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c
index 3365581..efc6c97 100644
--- a/drivers/net/hamradio/scc.c
+++ b/drivers/net/hamradio/scc.c
@@ -177,7 +177,6 @@
#include <net/ax25.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c
index 6963277..5a6412e 100644
--- a/drivers/net/hamradio/yam.c
+++ b/drivers/net/hamradio/yam.c
@@ -52,7 +52,6 @@
#include <linux/bitops.h>
#include <linux/random.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/firmware.h>
diff --git a/drivers/net/hippi/rrunner.c b/drivers/net/hippi/rrunner.c
index 2a51363..168c8f4 100644
--- a/drivers/net/hippi/rrunner.c
+++ b/drivers/net/hippi/rrunner.c
@@ -43,7 +43,6 @@
#include <linux/slab.h>
#include <net/sock.h>
-#include <asm/system.h>
#include <asm/cache.h>
#include <asm/byteorder.h>
#include <asm/io.h>
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
index e535137..4680478 100644
--- a/drivers/net/irda/Kconfig
+++ b/drivers/net/irda/Kconfig
@@ -356,7 +356,7 @@ config VLSI_FIR
config SA1100_FIR
tristate "SA1100 Internal IR"
- depends on ARCH_SA1100 && IRDA
+ depends on ARCH_SA1100 && IRDA && DMA_SA11X0
config VIA_FIR
tristate "VIA VT8231/VT1211 SIR/MIR/FIR"
diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c
index 617a446..4351296 100644
--- a/drivers/net/irda/donauboe.c
+++ b/drivers/net/irda/donauboe.c
@@ -156,7 +156,6 @@
#include <linux/pci.h>
#include <linux/rtnetlink.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <net/irda/wrapper.h>
diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c
index da27050..a0d1913 100644
--- a/drivers/net/irda/sa1100_ir.c
+++ b/drivers/net/irda/sa1100_ir.c
@@ -15,7 +15,7 @@
* This driver takes one kernel command line parameter, sa1100ir=, with
* the following options:
* max_rate:baudrate - set the maximum baud rate
- * power_leve:level - set the transmitter power level
+ * power_level:level - set the transmitter power level
* tx_lpm:0|1 - set transmit low power mode
*/
#include <linux/module.h>
@@ -30,13 +30,13 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/sa11x0-dma.h>
#include <net/irda/irda.h>
#include <net/irda/wrapper.h>
#include <net/irda/irda_device.h>
-#include <asm/irq.h>
-#include <mach/dma.h>
#include <mach/hardware.h>
#include <asm/mach/irda.h>
@@ -44,8 +44,15 @@ static int power_level = 3;
static int tx_lpm;
static int max_rate = 4000000;
+struct sa1100_buf {
+ struct device *dev;
+ struct sk_buff *skb;
+ struct scatterlist sg;
+ struct dma_chan *chan;
+ dma_cookie_t cookie;
+};
+
struct sa1100_irda {
- unsigned char hscr0;
unsigned char utcr4;
unsigned char power;
unsigned char open;
@@ -53,12 +60,8 @@ struct sa1100_irda {
int speed;
int newspeed;
- struct sk_buff *txskb;
- struct sk_buff *rxskb;
- dma_addr_t txbuf_dma;
- dma_addr_t rxbuf_dma;
- dma_regs_t *txdma;
- dma_regs_t *rxdma;
+ struct sa1100_buf dma_rx;
+ struct sa1100_buf dma_tx;
struct device *dev;
struct irda_platform_data *pdata;
@@ -67,23 +70,103 @@ struct sa1100_irda {
iobuff_t tx_buff;
iobuff_t rx_buff;
+
+ int (*tx_start)(struct sk_buff *, struct net_device *, struct sa1100_irda *);
+ irqreturn_t (*irq)(struct net_device *, struct sa1100_irda *);
};
+static int sa1100_irda_set_speed(struct sa1100_irda *, int);
+
#define IS_FIR(si) ((si)->speed >= 4000000)
#define HPSIR_MAX_RXLEN 2047
+static struct dma_slave_config sa1100_irda_sir_tx = {
+ .direction = DMA_TO_DEVICE,
+ .dst_addr = __PREG(Ser2UTDR),
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .dst_maxburst = 4,
+};
+
+static struct dma_slave_config sa1100_irda_fir_rx = {
+ .direction = DMA_FROM_DEVICE,
+ .src_addr = __PREG(Ser2HSDR),
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .src_maxburst = 8,
+};
+
+static struct dma_slave_config sa1100_irda_fir_tx = {
+ .direction = DMA_TO_DEVICE,
+ .dst_addr = __PREG(Ser2HSDR),
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .dst_maxburst = 8,
+};
+
+static unsigned sa1100_irda_dma_xferred(struct sa1100_buf *buf)
+{
+ struct dma_chan *chan = buf->chan;
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ status = chan->device->device_tx_status(chan, buf->cookie, &state);
+ if (status != DMA_PAUSED)
+ return 0;
+
+ return sg_dma_len(&buf->sg) - state.residue;
+}
+
+static int sa1100_irda_dma_request(struct device *dev, struct sa1100_buf *buf,
+ const char *name, struct dma_slave_config *cfg)
+{
+ dma_cap_mask_t m;
+ int ret;
+
+ dma_cap_zero(m);
+ dma_cap_set(DMA_SLAVE, m);
+
+ buf->chan = dma_request_channel(m, sa11x0_dma_filter_fn, (void *)name);
+ if (!buf->chan) {
+ dev_err(dev, "unable to request DMA channel for %s\n",
+ name);
+ return -ENOENT;
+ }
+
+ ret = dmaengine_slave_config(buf->chan, cfg);
+ if (ret)
+ dev_warn(dev, "DMA slave_config for %s returned %d\n",
+ name, ret);
+
+ buf->dev = buf->chan->device->dev;
+
+ return 0;
+}
+
+static void sa1100_irda_dma_start(struct sa1100_buf *buf,
+ enum dma_transfer_direction dir, dma_async_tx_callback cb, void *cb_p)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *chan = buf->chan;
+
+ desc = chan->device->device_prep_slave_sg(chan, &buf->sg, 1, dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (desc) {
+ desc->callback = cb;
+ desc->callback_param = cb_p;
+ buf->cookie = dmaengine_submit(desc);
+ dma_async_issue_pending(chan);
+ }
+}
+
/*
* Allocate and map the receive buffer, unless it is already allocated.
*/
static int sa1100_irda_rx_alloc(struct sa1100_irda *si)
{
- if (si->rxskb)
+ if (si->dma_rx.skb)
return 0;
- si->rxskb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC);
-
- if (!si->rxskb) {
+ si->dma_rx.skb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC);
+ if (!si->dma_rx.skb) {
printk(KERN_ERR "sa1100_ir: out of memory for RX SKB\n");
return -ENOMEM;
}
@@ -92,11 +175,14 @@ static int sa1100_irda_rx_alloc(struct sa1100_irda *si)
* Align any IP headers that may be contained
* within the frame.
*/
- skb_reserve(si->rxskb, 1);
+ skb_reserve(si->dma_rx.skb, 1);
+
+ sg_set_buf(&si->dma_rx.sg, si->dma_rx.skb->data, HPSIR_MAX_RXLEN);
+ if (dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE) == 0) {
+ dev_kfree_skb_any(si->dma_rx.skb);
+ return -ENOMEM;
+ }
- si->rxbuf_dma = dma_map_single(si->dev, si->rxskb->data,
- HPSIR_MAX_RXLEN,
- DMA_FROM_DEVICE);
return 0;
}
@@ -106,7 +192,7 @@ static int sa1100_irda_rx_alloc(struct sa1100_irda *si)
*/
static void sa1100_irda_rx_dma_start(struct sa1100_irda *si)
{
- if (!si->rxskb) {
+ if (!si->dma_rx.skb) {
printk(KERN_ERR "sa1100_ir: rx buffer went missing\n");
return;
}
@@ -114,254 +200,87 @@ static void sa1100_irda_rx_dma_start(struct sa1100_irda *si)
/*
* First empty receive FIFO
*/
- Ser2HSCR0 = si->hscr0 | HSCR0_HSSP;
+ Ser2HSCR0 = HSCR0_HSSP;
/*
* Enable the DMA, receiver and receive interrupt.
*/
- sa1100_clear_dma(si->rxdma);
- sa1100_start_dma(si->rxdma, si->rxbuf_dma, HPSIR_MAX_RXLEN);
- Ser2HSCR0 = si->hscr0 | HSCR0_HSSP | HSCR0_RXE;
+ dmaengine_terminate_all(si->dma_rx.chan);
+ sa1100_irda_dma_start(&si->dma_rx, DMA_DEV_TO_MEM, NULL, NULL);
+
+ Ser2HSCR0 = HSCR0_HSSP | HSCR0_RXE;
}
-/*
- * Set the IrDA communications speed.
- */
-static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed)
+static void sa1100_irda_check_speed(struct sa1100_irda *si)
{
- unsigned long flags;
- int brd, ret = -EINVAL;
-
- switch (speed) {
- case 9600: case 19200: case 38400:
- case 57600: case 115200:
- brd = 3686400 / (16 * speed) - 1;
-
- /*
- * Stop the receive DMA.
- */
- if (IS_FIR(si))
- sa1100_stop_dma(si->rxdma);
-
- local_irq_save(flags);
-
- Ser2UTCR3 = 0;
- Ser2HSCR0 = HSCR0_UART;
-
- Ser2UTCR1 = brd >> 8;
- Ser2UTCR2 = brd;
-
- /*
- * Clear status register
- */
- Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID;
- Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE;
-
- if (si->pdata->set_speed)
- si->pdata->set_speed(si->dev, speed);
-
- si->speed = speed;
-
- local_irq_restore(flags);
- ret = 0;
- break;
-
- case 4000000:
- local_irq_save(flags);
-
- si->hscr0 = 0;
-
- Ser2HSSR0 = 0xff;
- Ser2HSCR0 = si->hscr0 | HSCR0_HSSP;
- Ser2UTCR3 = 0;
-
- si->speed = speed;
-
- if (si->pdata->set_speed)
- si->pdata->set_speed(si->dev, speed);
-
- sa1100_irda_rx_alloc(si);
- sa1100_irda_rx_dma_start(si);
-
- local_irq_restore(flags);
-
- break;
-
- default:
- break;
+ if (si->newspeed) {
+ sa1100_irda_set_speed(si, si->newspeed);
+ si->newspeed = 0;
}
-
- return ret;
}
/*
- * Control the power state of the IrDA transmitter.
- * State:
- * 0 - off
- * 1 - short range, lowest power
- * 2 - medium range, medium power
- * 3 - maximum range, high power
- *
- * Currently, only assabet is known to support this.
+ * HP-SIR format support.
*/
-static int
-__sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state)
+static void sa1100_irda_sirtxdma_irq(void *id)
{
- int ret = 0;
- if (si->pdata->set_power)
- ret = si->pdata->set_power(si->dev, state);
- return ret;
-}
-
-static inline int
-sa1100_set_power(struct sa1100_irda *si, unsigned int state)
-{
- int ret;
-
- ret = __sa1100_irda_set_power(si, state);
- if (ret == 0)
- si->power = state;
+ struct net_device *dev = id;
+ struct sa1100_irda *si = netdev_priv(dev);
- return ret;
-}
+ dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE);
+ dev_kfree_skb(si->dma_tx.skb);
+ si->dma_tx.skb = NULL;
-static int sa1100_irda_startup(struct sa1100_irda *si)
-{
- int ret;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += sg_dma_len(&si->dma_tx.sg);
- /*
- * Ensure that the ports for this device are setup correctly.
- */
- if (si->pdata->startup) {
- ret = si->pdata->startup(si->dev);
- if (ret)
- return ret;
- }
-
- /*
- * Configure PPC for IRDA - we want to drive TXD2 low.
- * We also want to drive this pin low during sleep.
- */
- PPSR &= ~PPC_TXD2;
- PSDR &= ~PPC_TXD2;
- PPDR |= PPC_TXD2;
-
- /*
- * Enable HP-SIR modulation, and ensure that the port is disabled.
- */
- Ser2UTCR3 = 0;
- Ser2HSCR0 = HSCR0_UART;
- Ser2UTCR4 = si->utcr4;
- Ser2UTCR0 = UTCR0_8BitData;
- Ser2HSCR2 = HSCR2_TrDataH | HSCR2_RcDataL;
+ /* We need to ensure that the transmitter has finished. */
+ do
+ rmb();
+ while (Ser2UTSR1 & UTSR1_TBY);
/*
- * Clear status register
+ * Ok, we've finished transmitting. Now enable the receiver.
+ * Sometimes we get a receive IRQ immediately after a transmit...
*/
Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID;
+ Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE;
- ret = sa1100_irda_set_speed(si, si->speed = 9600);
- if (ret) {
- Ser2UTCR3 = 0;
- Ser2HSCR0 = 0;
-
- if (si->pdata->shutdown)
- si->pdata->shutdown(si->dev);
- }
-
- return ret;
-}
-
-static void sa1100_irda_shutdown(struct sa1100_irda *si)
-{
- /*
- * Stop all DMA activity.
- */
- sa1100_stop_dma(si->rxdma);
- sa1100_stop_dma(si->txdma);
-
- /* Disable the port. */
- Ser2UTCR3 = 0;
- Ser2HSCR0 = 0;
+ sa1100_irda_check_speed(si);
- if (si->pdata->shutdown)
- si->pdata->shutdown(si->dev);
+ /* I'm hungry! */
+ netif_wake_queue(dev);
}
-#ifdef CONFIG_PM
-/*
- * Suspend the IrDA interface.
- */
-static int sa1100_irda_suspend(struct platform_device *pdev, pm_message_t state)
+static int sa1100_irda_sir_tx_start(struct sk_buff *skb, struct net_device *dev,
+ struct sa1100_irda *si)
{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct sa1100_irda *si;
-
- if (!dev)
- return 0;
-
- si = netdev_priv(dev);
- if (si->open) {
- /*
- * Stop the transmit queue
- */
- netif_device_detach(dev);
- disable_irq(dev->irq);
- sa1100_irda_shutdown(si);
- __sa1100_irda_set_power(si, 0);
+ si->tx_buff.data = si->tx_buff.head;
+ si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data,
+ si->tx_buff.truesize);
+
+ si->dma_tx.skb = skb;
+ sg_set_buf(&si->dma_tx.sg, si->tx_buff.data, si->tx_buff.len);
+ if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) {
+ si->dma_tx.skb = NULL;
+ netif_wake_queue(dev);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
}
- return 0;
-}
-
-/*
- * Resume the IrDA interface.
- */
-static int sa1100_irda_resume(struct platform_device *pdev)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct sa1100_irda *si;
-
- if (!dev)
- return 0;
+ sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_sirtxdma_irq, dev);
- si = netdev_priv(dev);
- if (si->open) {
- /*
- * If we missed a speed change, initialise at the new speed
- * directly. It is debatable whether this is actually
- * required, but in the interests of continuing from where
- * we left off it is desirable. The converse argument is
- * that we should re-negotiate at 9600 baud again.
- */
- if (si->newspeed) {
- si->speed = si->newspeed;
- si->newspeed = 0;
- }
-
- sa1100_irda_startup(si);
- __sa1100_irda_set_power(si, si->power);
- enable_irq(dev->irq);
-
- /*
- * This automatically wakes up the queue
- */
- netif_device_attach(dev);
- }
+ /*
+ * The mean turn-around time is enforced by XBOF padding,
+ * so we don't have to do anything special here.
+ */
+ Ser2UTCR3 = UTCR3_TXE;
- return 0;
+ return NETDEV_TX_OK;
}
-#else
-#define sa1100_irda_suspend NULL
-#define sa1100_irda_resume NULL
-#endif
-/*
- * HP-SIR format interrupt service routines.
- */
-static void sa1100_irda_hpsir_irq(struct net_device *dev)
+static irqreturn_t sa1100_irda_sir_irq(struct net_device *dev, struct sa1100_irda *si)
{
- struct sa1100_irda *si = netdev_priv(dev);
int status;
status = Ser2UTSR0;
@@ -414,51 +333,96 @@ static void sa1100_irda_hpsir_irq(struct net_device *dev)
}
- if (status & UTSR0_TFS && si->tx_buff.len) {
- /*
- * Transmitter FIFO is not full
- */
- do {
- Ser2UTDR = *si->tx_buff.data++;
- si->tx_buff.len -= 1;
- } while (Ser2UTSR1 & UTSR1_TNF && si->tx_buff.len);
+ return IRQ_HANDLED;
+}
- if (si->tx_buff.len == 0) {
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += si->tx_buff.data -
- si->tx_buff.head;
+/*
+ * FIR format support.
+ */
+static void sa1100_irda_firtxdma_irq(void *id)
+{
+ struct net_device *dev = id;
+ struct sa1100_irda *si = netdev_priv(dev);
+ struct sk_buff *skb;
- /*
- * We need to ensure that the transmitter has
- * finished.
- */
- do
- rmb();
- while (Ser2UTSR1 & UTSR1_TBY);
+ /*
+ * Wait for the transmission to complete. Unfortunately,
+ * the hardware doesn't give us an interrupt to indicate
+ * "end of frame".
+ */
+ do
+ rmb();
+ while (!(Ser2HSSR0 & HSSR0_TUR) || Ser2HSSR1 & HSSR1_TBY);
- /*
- * Ok, we've finished transmitting. Now enable
- * the receiver. Sometimes we get a receive IRQ
- * immediately after a transmit...
- */
- Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID;
- Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE;
+ /*
+ * Clear the transmit underrun bit.
+ */
+ Ser2HSSR0 = HSSR0_TUR;
- if (si->newspeed) {
- sa1100_irda_set_speed(si, si->newspeed);
- si->newspeed = 0;
- }
+ /*
+ * Do we need to change speed? Note that we're lazy
+ * here - we don't free the old dma_rx.skb. We don't need
+ * to allocate a buffer either.
+ */
+ sa1100_irda_check_speed(si);
- /* I'm hungry! */
- netif_wake_queue(dev);
- }
+ /*
+ * Start reception. This disables the transmitter for
+ * us. This will be using the existing RX buffer.
+ */
+ sa1100_irda_rx_dma_start(si);
+
+ /* Account and free the packet. */
+ skb = si->dma_tx.skb;
+ if (skb) {
+ dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1,
+ DMA_TO_DEVICE);
+ dev->stats.tx_packets ++;
+ dev->stats.tx_bytes += skb->len;
+ dev_kfree_skb_irq(skb);
+ si->dma_tx.skb = NULL;
}
+
+ /*
+ * Make sure that the TX queue is available for sending
+ * (for retries). TX has priority over RX at all times.
+ */
+ netif_wake_queue(dev);
+}
+
+static int sa1100_irda_fir_tx_start(struct sk_buff *skb, struct net_device *dev,
+ struct sa1100_irda *si)
+{
+ int mtt = irda_get_mtt(skb);
+
+ si->dma_tx.skb = skb;
+ sg_set_buf(&si->dma_tx.sg, skb->data, skb->len);
+ if (dma_map_sg(si->dma_tx.dev, &si->dma_tx.sg, 1, DMA_TO_DEVICE) == 0) {
+ si->dma_tx.skb = NULL;
+ netif_wake_queue(dev);
+ dev->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ sa1100_irda_dma_start(&si->dma_tx, DMA_MEM_TO_DEV, sa1100_irda_firtxdma_irq, dev);
+
+ /*
+ * If we have a mean turn-around time, impose the specified
+ * specified delay. We could shorten this by timing from
+ * the point we received the packet.
+ */
+ if (mtt)
+ udelay(mtt);
+
+ Ser2HSCR0 = HSCR0_HSSP | HSCR0_TXE;
+
+ return NETDEV_TX_OK;
}
static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev)
{
- struct sk_buff *skb = si->rxskb;
- dma_addr_t dma_addr;
+ struct sk_buff *skb = si->dma_rx.skb;
unsigned int len, stat, data;
if (!skb) {
@@ -469,11 +433,10 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev
/*
* Get the current data position.
*/
- dma_addr = sa1100_get_dma_pos(si->rxdma);
- len = dma_addr - si->rxbuf_dma;
+ len = sa1100_irda_dma_xferred(&si->dma_rx);
if (len > HPSIR_MAX_RXLEN)
len = HPSIR_MAX_RXLEN;
- dma_unmap_single(si->dev, si->rxbuf_dma, len, DMA_FROM_DEVICE);
+ dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE);
do {
/*
@@ -501,7 +464,7 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev
} while (Ser2HSSR0 & HSSR0_EIF);
if (stat & HSSR1_EOF) {
- si->rxskb = NULL;
+ si->dma_rx.skb = NULL;
skb_put(skb, len);
skb->dev = dev;
@@ -518,28 +481,23 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev
netif_rx(skb);
} else {
/*
- * Remap the buffer.
+ * Remap the buffer - it was previously mapped, and we
+ * hope that this succeeds.
*/
- si->rxbuf_dma = dma_map_single(si->dev, si->rxskb->data,
- HPSIR_MAX_RXLEN,
- DMA_FROM_DEVICE);
+ dma_map_sg(si->dma_rx.dev, &si->dma_rx.sg, 1, DMA_FROM_DEVICE);
}
}
/*
- * FIR format interrupt service routine. We only have to
- * handle RX events; transmit events go via the TX DMA handler.
- *
- * No matter what, we disable RX, process, and the restart RX.
+ * We only have to handle RX events here; transmit events go via the TX
+ * DMA handler. We disable RX, process, and the restart RX.
*/
-static void sa1100_irda_fir_irq(struct net_device *dev)
+static irqreturn_t sa1100_irda_fir_irq(struct net_device *dev, struct sa1100_irda *si)
{
- struct sa1100_irda *si = netdev_priv(dev);
-
/*
* Stop RX DMA
*/
- sa1100_stop_dma(si->rxdma);
+ dmaengine_pause(si->dma_rx.chan);
/*
* Framing error - we throw away the packet completely.
@@ -555,7 +513,7 @@ static void sa1100_irda_fir_irq(struct net_device *dev)
/*
* Clear out the DMA...
*/
- Ser2HSCR0 = si->hscr0 | HSCR0_HSSP;
+ Ser2HSCR0 = HSCR0_HSSP;
/*
* Clear selected status bits now, so we
@@ -577,74 +535,124 @@ static void sa1100_irda_fir_irq(struct net_device *dev)
* No matter what happens, we must restart reception.
*/
sa1100_irda_rx_dma_start(si);
-}
-static irqreturn_t sa1100_irda_irq(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- if (IS_FIR(((struct sa1100_irda *)netdev_priv(dev))))
- sa1100_irda_fir_irq(dev);
- else
- sa1100_irda_hpsir_irq(dev);
return IRQ_HANDLED;
}
/*
- * TX DMA completion handler.
+ * Set the IrDA communications speed.
*/
-static void sa1100_irda_txdma_irq(void *id)
+static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed)
{
- struct net_device *dev = id;
- struct sa1100_irda *si = netdev_priv(dev);
- struct sk_buff *skb = si->txskb;
+ unsigned long flags;
+ int brd, ret = -EINVAL;
- si->txskb = NULL;
+ switch (speed) {
+ case 9600: case 19200: case 38400:
+ case 57600: case 115200:
+ brd = 3686400 / (16 * speed) - 1;
- /*
- * Wait for the transmission to complete. Unfortunately,
- * the hardware doesn't give us an interrupt to indicate
- * "end of frame".
- */
- do
- rmb();
- while (!(Ser2HSSR0 & HSSR0_TUR) || Ser2HSSR1 & HSSR1_TBY);
+ /* Stop the receive DMA, and configure transmit. */
+ if (IS_FIR(si)) {
+ dmaengine_terminate_all(si->dma_rx.chan);
+ dmaengine_slave_config(si->dma_tx.chan,
+ &sa1100_irda_sir_tx);
+ }
- /*
- * Clear the transmit underrun bit.
- */
- Ser2HSSR0 = HSSR0_TUR;
+ local_irq_save(flags);
- /*
- * Do we need to change speed? Note that we're lazy
- * here - we don't free the old rxskb. We don't need
- * to allocate a buffer either.
- */
- if (si->newspeed) {
- sa1100_irda_set_speed(si, si->newspeed);
- si->newspeed = 0;
- }
+ Ser2UTCR3 = 0;
+ Ser2HSCR0 = HSCR0_UART;
- /*
- * Start reception. This disables the transmitter for
- * us. This will be using the existing RX buffer.
- */
- sa1100_irda_rx_dma_start(si);
+ Ser2UTCR1 = brd >> 8;
+ Ser2UTCR2 = brd;
- /*
- * Account and free the packet.
- */
- if (skb) {
- dma_unmap_single(si->dev, si->txbuf_dma, skb->len, DMA_TO_DEVICE);
- dev->stats.tx_packets ++;
- dev->stats.tx_bytes += skb->len;
- dev_kfree_skb_irq(skb);
+ /*
+ * Clear status register
+ */
+ Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID;
+ Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE;
+
+ if (si->pdata->set_speed)
+ si->pdata->set_speed(si->dev, speed);
+
+ si->speed = speed;
+ si->tx_start = sa1100_irda_sir_tx_start;
+ si->irq = sa1100_irda_sir_irq;
+
+ local_irq_restore(flags);
+ ret = 0;
+ break;
+
+ case 4000000:
+ if (!IS_FIR(si))
+ dmaengine_slave_config(si->dma_tx.chan,
+ &sa1100_irda_fir_tx);
+
+ local_irq_save(flags);
+
+ Ser2HSSR0 = 0xff;
+ Ser2HSCR0 = HSCR0_HSSP;
+ Ser2UTCR3 = 0;
+
+ si->speed = speed;
+ si->tx_start = sa1100_irda_fir_tx_start;
+ si->irq = sa1100_irda_fir_irq;
+
+ if (si->pdata->set_speed)
+ si->pdata->set_speed(si->dev, speed);
+
+ sa1100_irda_rx_alloc(si);
+ sa1100_irda_rx_dma_start(si);
+
+ local_irq_restore(flags);
+
+ break;
+
+ default:
+ break;
}
- /*
- * Make sure that the TX queue is available for sending
- * (for retries). TX has priority over RX at all times.
- */
- netif_wake_queue(dev);
+ return ret;
+}
+
+/*
+ * Control the power state of the IrDA transmitter.
+ * State:
+ * 0 - off
+ * 1 - short range, lowest power
+ * 2 - medium range, medium power
+ * 3 - maximum range, high power
+ *
+ * Currently, only assabet is known to support this.
+ */
+static int
+__sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state)
+{
+ int ret = 0;
+ if (si->pdata->set_power)
+ ret = si->pdata->set_power(si->dev, state);
+ return ret;
+}
+
+static inline int
+sa1100_set_power(struct sa1100_irda *si, unsigned int state)
+{
+ int ret;
+
+ ret = __sa1100_irda_set_power(si, state);
+ if (ret == 0)
+ si->power = state;
+
+ return ret;
+}
+
+static irqreturn_t sa1100_irda_irq(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct sa1100_irda *si = netdev_priv(dev);
+
+ return si->irq(dev, si);
}
static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
@@ -660,62 +668,19 @@ static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
if (speed != si->speed && speed != -1)
si->newspeed = speed;
- /*
- * If this is an empty frame, we can bypass a lot.
- */
+ /* If this is an empty frame, we can bypass a lot. */
if (skb->len == 0) {
- if (si->newspeed) {
- si->newspeed = 0;
- sa1100_irda_set_speed(si, speed);
- }
+ sa1100_irda_check_speed(si);
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
- if (!IS_FIR(si)) {
- netif_stop_queue(dev);
-
- si->tx_buff.data = si->tx_buff.head;
- si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data,
- si->tx_buff.truesize);
-
- /*
- * Set the transmit interrupt enable. This will fire
- * off an interrupt immediately. Note that we disable
- * the receiver so we won't get spurious characteres
- * received.
- */
- Ser2UTCR3 = UTCR3_TIE | UTCR3_TXE;
-
- dev_kfree_skb(skb);
- } else {
- int mtt = irda_get_mtt(skb);
-
- /*
- * We must not be transmitting...
- */
- BUG_ON(si->txskb);
-
- netif_stop_queue(dev);
-
- si->txskb = skb;
- si->txbuf_dma = dma_map_single(si->dev, skb->data,
- skb->len, DMA_TO_DEVICE);
-
- sa1100_start_dma(si->txdma, si->txbuf_dma, skb->len);
-
- /*
- * If we have a mean turn-around time, impose the specified
- * specified delay. We could shorten this by timing from
- * the point we received the packet.
- */
- if (mtt)
- udelay(mtt);
+ netif_stop_queue(dev);
- Ser2HSCR0 = si->hscr0 | HSCR0_HSSP | HSCR0_TXE;
- }
+ /* We must not already have a skb to transmit... */
+ BUG_ON(si->dma_tx.skb);
- return NETDEV_TX_OK;
+ return si->tx_start(skb, dev, si);
}
static int
@@ -762,6 +727,69 @@ sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
return ret;
}
+static int sa1100_irda_startup(struct sa1100_irda *si)
+{
+ int ret;
+
+ /*
+ * Ensure that the ports for this device are setup correctly.
+ */
+ if (si->pdata->startup) {
+ ret = si->pdata->startup(si->dev);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Configure PPC for IRDA - we want to drive TXD2 low.
+ * We also want to drive this pin low during sleep.
+ */
+ PPSR &= ~PPC_TXD2;
+ PSDR &= ~PPC_TXD2;
+ PPDR |= PPC_TXD2;
+
+ /*
+ * Enable HP-SIR modulation, and ensure that the port is disabled.
+ */
+ Ser2UTCR3 = 0;
+ Ser2HSCR0 = HSCR0_UART;
+ Ser2UTCR4 = si->utcr4;
+ Ser2UTCR0 = UTCR0_8BitData;
+ Ser2HSCR2 = HSCR2_TrDataH | HSCR2_RcDataL;
+
+ /*
+ * Clear status register
+ */
+ Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID;
+
+ ret = sa1100_irda_set_speed(si, si->speed = 9600);
+ if (ret) {
+ Ser2UTCR3 = 0;
+ Ser2HSCR0 = 0;
+
+ if (si->pdata->shutdown)
+ si->pdata->shutdown(si->dev);
+ }
+
+ return ret;
+}
+
+static void sa1100_irda_shutdown(struct sa1100_irda *si)
+{
+ /*
+ * Stop all DMA activity.
+ */
+ dmaengine_terminate_all(si->dma_rx.chan);
+ dmaengine_terminate_all(si->dma_tx.chan);
+
+ /* Disable the port. */
+ Ser2UTCR3 = 0;
+ Ser2HSCR0 = 0;
+
+ if (si->pdata->shutdown)
+ si->pdata->shutdown(si->dev);
+}
+
static int sa1100_irda_start(struct net_device *dev)
{
struct sa1100_irda *si = netdev_priv(dev);
@@ -769,26 +797,17 @@ static int sa1100_irda_start(struct net_device *dev)
si->speed = 9600;
- err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev);
- if (err)
- goto err_irq;
-
- err = sa1100_request_dma(DMA_Ser2HSSPRd, "IrDA receive",
- NULL, NULL, &si->rxdma);
+ err = sa1100_irda_dma_request(si->dev, &si->dma_rx, "Ser2ICPRc",
+ &sa1100_irda_fir_rx);
if (err)
goto err_rx_dma;
- err = sa1100_request_dma(DMA_Ser2HSSPWr, "IrDA transmit",
- sa1100_irda_txdma_irq, dev, &si->txdma);
+ err = sa1100_irda_dma_request(si->dev, &si->dma_tx, "Ser2ICPTr",
+ &sa1100_irda_sir_tx);
if (err)
goto err_tx_dma;
/*
- * The interrupt must remain disabled for now.
- */
- disable_irq(dev->irq);
-
- /*
* Setup the serial port for the specified speed.
*/
err = sa1100_irda_startup(si);
@@ -803,44 +822,60 @@ static int sa1100_irda_start(struct net_device *dev)
if (!si->irlap)
goto err_irlap;
+ err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev);
+ if (err)
+ goto err_irq;
+
/*
* Now enable the interrupt and start the queue
*/
si->open = 1;
sa1100_set_power(si, power_level); /* low power mode */
- enable_irq(dev->irq);
+
netif_start_queue(dev);
return 0;
+err_irq:
+ irlap_close(si->irlap);
err_irlap:
si->open = 0;
sa1100_irda_shutdown(si);
err_startup:
- sa1100_free_dma(si->txdma);
+ dma_release_channel(si->dma_tx.chan);
err_tx_dma:
- sa1100_free_dma(si->rxdma);
+ dma_release_channel(si->dma_rx.chan);
err_rx_dma:
- free_irq(dev->irq, dev);
-err_irq:
return err;
}
static int sa1100_irda_stop(struct net_device *dev)
{
struct sa1100_irda *si = netdev_priv(dev);
+ struct sk_buff *skb;
+
+ netif_stop_queue(dev);
- disable_irq(dev->irq);
+ si->open = 0;
sa1100_irda_shutdown(si);
/*
- * If we have been doing DMA receive, make sure we
+ * If we have been doing any DMA activity, make sure we
* tidy that up cleanly.
*/
- if (si->rxskb) {
- dma_unmap_single(si->dev, si->rxbuf_dma, HPSIR_MAX_RXLEN,
- DMA_FROM_DEVICE);
- dev_kfree_skb(si->rxskb);
- si->rxskb = NULL;
+ skb = si->dma_rx.skb;
+ if (skb) {
+ dma_unmap_sg(si->dma_rx.dev, &si->dma_rx.sg, 1,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb(skb);
+ si->dma_rx.skb = NULL;
+ }
+
+ skb = si->dma_tx.skb;
+ if (skb) {
+ dma_unmap_sg(si->dma_tx.dev, &si->dma_tx.sg, 1,
+ DMA_TO_DEVICE);
+ dev_kfree_skb(skb);
+ si->dma_tx.skb = NULL;
}
/* Stop IrLAP */
@@ -849,14 +884,11 @@ static int sa1100_irda_stop(struct net_device *dev)
si->irlap = NULL;
}
- netif_stop_queue(dev);
- si->open = 0;
-
/*
* Free resources
*/
- sa1100_free_dma(si->txdma);
- sa1100_free_dma(si->rxdma);
+ dma_release_channel(si->dma_tx.chan);
+ dma_release_channel(si->dma_rx.chan);
free_irq(dev->irq, dev);
sa1100_set_power(si, 0);
@@ -888,11 +920,15 @@ static int sa1100_irda_probe(struct platform_device *pdev)
struct net_device *dev;
struct sa1100_irda *si;
unsigned int baudrate_mask;
- int err;
+ int err, irq;
if (!pdev->dev.platform_data)
return -EINVAL;
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return irq < 0 ? irq : -ENXIO;
+
err = request_mem_region(__PREG(Ser2UTCR0), 0x24, "IrDA") ? 0 : -EBUSY;
if (err)
goto err_mem_1;
@@ -907,22 +943,27 @@ static int sa1100_irda_probe(struct platform_device *pdev)
if (!dev)
goto err_mem_4;
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
si = netdev_priv(dev);
si->dev = &pdev->dev;
si->pdata = pdev->dev.platform_data;
+ sg_init_table(&si->dma_rx.sg, 1);
+ sg_init_table(&si->dma_tx.sg, 1);
+
/*
* Initialise the HP-SIR buffers
*/
err = sa1100_irda_init_iobuf(&si->rx_buff, 14384);
if (err)
goto err_mem_5;
- err = sa1100_irda_init_iobuf(&si->tx_buff, 4000);
+ err = sa1100_irda_init_iobuf(&si->tx_buff, IRDA_SIR_MAX_FRAME);
if (err)
goto err_mem_5;
dev->netdev_ops = &sa1100_irda_netdev_ops;
- dev->irq = IRQ_Ser2ICP;
+ dev->irq = irq;
irda_init_max_qos_capabilies(&si->qos);
@@ -996,6 +1037,74 @@ static int sa1100_irda_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+/*
+ * Suspend the IrDA interface.
+ */
+static int sa1100_irda_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct sa1100_irda *si;
+
+ if (!dev)
+ return 0;
+
+ si = netdev_priv(dev);
+ if (si->open) {
+ /*
+ * Stop the transmit queue
+ */
+ netif_device_detach(dev);
+ disable_irq(dev->irq);
+ sa1100_irda_shutdown(si);
+ __sa1100_irda_set_power(si, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * Resume the IrDA interface.
+ */
+static int sa1100_irda_resume(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct sa1100_irda *si;
+
+ if (!dev)
+ return 0;
+
+ si = netdev_priv(dev);
+ if (si->open) {
+ /*
+ * If we missed a speed change, initialise at the new speed
+ * directly. It is debatable whether this is actually
+ * required, but in the interests of continuing from where
+ * we left off it is desirable. The converse argument is
+ * that we should re-negotiate at 9600 baud again.
+ */
+ if (si->newspeed) {
+ si->speed = si->newspeed;
+ si->newspeed = 0;
+ }
+
+ sa1100_irda_startup(si);
+ __sa1100_irda_set_power(si, si->power);
+ enable_irq(dev->irq);
+
+ /*
+ * This automatically wakes up the queue
+ */
+ netif_device_attach(dev);
+ }
+
+ return 0;
+}
+#else
+#define sa1100_irda_suspend NULL
+#define sa1100_irda_resume NULL
+#endif
+
static struct platform_driver sa1100ir_driver = {
.probe = sa1100_irda_probe,
.remove = sa1100_irda_remove,
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index b71998d..32eb94e 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -41,7 +41,6 @@
#include <linux/in.h>
#include <linux/init.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
diff --git a/drivers/net/plip/plip.c b/drivers/net/plip/plip.c
index 1a5a316..bed62d9 100644
--- a/drivers/net/plip/plip.c
+++ b/drivers/net/plip/plip.c
@@ -113,7 +113,6 @@ static const char version[] = "NET3 PLIP version 2.4-parport gniibe@mri.co.jp\n"
#include <net/neighbour.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
diff --git a/drivers/net/slip/slhc.c b/drivers/net/slip/slhc.c
index 0a0a664..1252d9c 100644
--- a/drivers/net/slip/slhc.c
+++ b/drivers/net/slip/slhc.c
@@ -75,7 +75,6 @@
#include <linux/skbuff.h>
#include <net/sock.h>
#include <linux/timer.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <net/checksum.h>
#include <asm/unaligned.h>
diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c
index 69345df..d4c9db3 100644
--- a/drivers/net/slip/slip.c
+++ b/drivers/net/slip/slip.c
@@ -64,7 +64,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/bitops.h>
#include <linux/sched.h>
diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c
index d7c292a..b15ac81 100644
--- a/drivers/net/tokenring/3c359.c
+++ b/drivers/net/tokenring/3c359.c
@@ -68,7 +68,6 @@
#include <net/checksum.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "3c359.h"
diff --git a/drivers/net/tokenring/abyss.c b/drivers/net/tokenring/abyss.c
index 515f122..b715e6b 100644
--- a/drivers/net/tokenring/abyss.c
+++ b/drivers/net/tokenring/abyss.c
@@ -33,7 +33,6 @@
#include <linux/netdevice.h>
#include <linux/trdevice.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
diff --git a/drivers/net/tokenring/ibmtr_cs.c b/drivers/net/tokenring/ibmtr_cs.c
index 91b6846..356e28e 100644
--- a/drivers/net/tokenring/ibmtr_cs.c
+++ b/drivers/net/tokenring/ibmtr_cs.c
@@ -63,7 +63,6 @@
#include <asm/uaccess.h>
#include <asm/io.h>
-#include <asm/system.h>
#define PCMCIA
#include "ibmtr.c"
diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c
index 8d71e0d..3e4b4f0 100644
--- a/drivers/net/tokenring/lanstreamer.c
+++ b/drivers/net/tokenring/lanstreamer.c
@@ -127,7 +127,6 @@
#include <net/checksum.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "lanstreamer.h"
diff --git a/drivers/net/tokenring/madgemc.c b/drivers/net/tokenring/madgemc.c
index 1cdc034..28adcdf 100644
--- a/drivers/net/tokenring/madgemc.c
+++ b/drivers/net/tokenring/madgemc.c
@@ -28,7 +28,6 @@ static const char version[] = "madgemc.c: v0.91 23/01/2000 by Adam Fritzler\n";
#include <linux/netdevice.h>
#include <linux/trdevice.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c
index fd8dce9..0e23474 100644
--- a/drivers/net/tokenring/olympic.c
+++ b/drivers/net/tokenring/olympic.c
@@ -106,7 +106,6 @@
#include <net/net_namespace.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "olympic.h"
diff --git a/drivers/net/tokenring/proteon.c b/drivers/net/tokenring/proteon.c
index 8d362e6..62d90e4 100644
--- a/drivers/net/tokenring/proteon.c
+++ b/drivers/net/tokenring/proteon.c
@@ -31,7 +31,6 @@ static const char version[] = "proteon.c: v1.00 02/01/2003 by Jochen Friedrich\n
#include <linux/trdevice.h>
#include <linux/platform_device.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pci.h>
diff --git a/drivers/net/tokenring/skisa.c b/drivers/net/tokenring/skisa.c
index 46db5c5..ee11e93 100644
--- a/drivers/net/tokenring/skisa.c
+++ b/drivers/net/tokenring/skisa.c
@@ -38,7 +38,6 @@ static const char version[] = "skisa.c: v1.03 09/12/2002 by Jochen Friedrich\n";
#include <linux/trdevice.h>
#include <linux/platform_device.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pci.h>
diff --git a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c
index 029846a..cb35fb7 100644
--- a/drivers/net/tokenring/smctr.c
+++ b/drivers/net/tokenring/smctr.c
@@ -49,7 +49,6 @@
#include <linux/bitops.h>
#include <linux/firmware.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/irq.h>
diff --git a/drivers/net/tokenring/tms380tr.c b/drivers/net/tokenring/tms380tr.c
index 102f896..be4813e 100644
--- a/drivers/net/tokenring/tms380tr.c
+++ b/drivers/net/tokenring/tms380tr.c
@@ -98,7 +98,6 @@ static const char version[] = "tms380tr.c: v1.10 30/12/2002 by Christoph Goos, A
#include <linux/firmware.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/irq.h>
diff --git a/drivers/net/tokenring/tmspci.c b/drivers/net/tokenring/tmspci.c
index d3e788a..fb9918d 100644
--- a/drivers/net/tokenring/tmspci.c
+++ b/drivers/net/tokenring/tmspci.c
@@ -34,7 +34,6 @@
#include <linux/netdevice.h>
#include <linux/trdevice.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 74d7f76..bb8c72c 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -69,7 +69,6 @@
#include <net/rtnetlink.h>
#include <net/sock.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
/* Uncomment to enable debugging */
diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig
index 423eb26..d70ede7 100644
--- a/drivers/net/wan/Kconfig
+++ b/drivers/net/wan/Kconfig
@@ -290,8 +290,8 @@ config FARSYNC
Frame Relay or X.25/LAPB.
If you want the module to be automatically loaded when the interface
- is referenced then you should add "alias hdlcX farsync" to
- /etc/modprobe.conf for each interface, where X is 0, 1, 2, ..., or
+ is referenced then you should add "alias hdlcX farsync" to a file
+ in /etc/modprobe.d/ for each interface, where X is 0, 1, 2, ..., or
simply use "alias hdlc* farsync" to indicate all of them.
To compile this driver as a module, choose M here: the
diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c
index 48ab38a..147614e 100644
--- a/drivers/net/wan/dlci.c
+++ b/drivers/net/wan/dlci.c
@@ -50,7 +50,6 @@
#include <net/sock.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c
index fe8d060..c676de7 100644
--- a/drivers/net/wan/dscc4.c
+++ b/drivers/net/wan/dscc4.c
@@ -93,7 +93,6 @@
#include <linux/mm.h>
#include <linux/slab.h>
-#include <asm/system.h>
#include <asm/cache.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/wan/hd64570.c b/drivers/net/wan/hd64570.c
index 33b67d8..cf49033 100644
--- a/drivers/net/wan/hd64570.c
+++ b/drivers/net/wan/hd64570.c
@@ -40,7 +40,6 @@
#include <linux/string.h>
#include <linux/types.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include "hd64570.h"
diff --git a/drivers/net/wan/hd64572.c b/drivers/net/wan/hd64572.c
index efc0db1..e2779fa 100644
--- a/drivers/net/wan/hd64572.c
+++ b/drivers/net/wan/hd64572.c
@@ -40,7 +40,6 @@
#include <linux/string.h>
#include <linux/types.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include "hd64572.h"
diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c
index 7beeb9b..a73b49e 100644
--- a/drivers/net/wan/lapbether.c
+++ b/drivers/net/wan/lapbether.c
@@ -35,7 +35,6 @@
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c
index c853161..de3bbf4 100644
--- a/drivers/net/wan/sdla.c
+++ b/drivers/net/wan/sdla.c
@@ -54,7 +54,6 @@
#include <linux/sdla.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c
index e862369..d7a65e1 100644
--- a/drivers/net/wan/x25_asy.c
+++ b/drivers/net/wan/x25_asy.c
@@ -18,7 +18,6 @@
#include <linux/module.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/string.h>
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index ddc061d..520a4b2 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -37,7 +37,6 @@
#include <linux/scatterlist.h>
#include <linux/crypto.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#include <linux/netdevice.h>
diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c
index c983c10..630577d 100644
--- a/drivers/net/wireless/airo_cs.c
+++ b/drivers/net/wireless/airo_cs.c
@@ -37,7 +37,6 @@
#include <pcmcia/ds.h>
#include <linux/io.h>
-#include <asm/system.h>
#include "airo.h"
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index 3010cee..6c87a82 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -50,7 +50,6 @@
#include <linux/timer.h>
#include <asm/byteorder.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/module.h>
#include <linux/netdevice.h>
diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c
index ec295c4..ded03d2 100644
--- a/drivers/net/wireless/atmel_cs.c
+++ b/drivers/net/wireless/atmel_cs.c
@@ -48,7 +48,6 @@
#include <pcmcia/ciscode.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <linux/wireless.h>
#include "atmel.h"
diff --git a/drivers/net/wireless/prism54/islpci_mgt.c b/drivers/net/wireless/prism54/islpci_mgt.c
index 851fa10..c5404cb 100644
--- a/drivers/net/wireless/prism54/islpci_mgt.c
+++ b/drivers/net/wireless/prism54/islpci_mgt.c
@@ -24,7 +24,6 @@
#include <linux/slab.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <linux/if_arp.h>
#include "prismcompat.h"
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index 04fec1f..86a738b 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -53,7 +53,6 @@
#include <net/iw_handler.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c
index 98fbf54..00f6e69 100644
--- a/drivers/net/wireless/wl3501_cs.c
+++ b/drivers/net/wireless/wl3501_cs.c
@@ -53,7 +53,6 @@
#include <asm/io.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#include "wl3501.h"
diff --git a/drivers/nubus/nubus.c b/drivers/nubus/nubus.c
index b764ac22d..44d01af 100644
--- a/drivers/nubus/nubus.c
+++ b/drivers/nubus/nubus.c
@@ -17,7 +17,6 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/setup.h>
-#include <asm/system.h>
#include <asm/page.h>
#include <asm/hwtest.h>
#include <linux/proc_fs.h>
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 6ea51dc..8e84ce9 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -91,4 +91,8 @@ config OF_PCI_IRQ
help
OpenFirmware PCI IRQ routing helpers
+config OF_MTD
+ depends on MTD
+ def_bool y
+
endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index a73f5a5..aa90e60 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_OF_SELFTEST) += selftest.o
obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
+obj-$(CONFIG_OF_MTD) += of_mtd.o
diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c
index 7e62d15..bba8121 100644
--- a/drivers/of/gpio.c
+++ b/drivers/of/gpio.c
@@ -78,8 +78,9 @@ err0:
EXPORT_SYMBOL(of_get_named_gpio_flags);
/**
- * of_gpio_count - Count GPIOs for a device
+ * of_gpio_named_count - Count GPIOs for a device
* @np: device node to count GPIOs for
+ * @propname: property name containing gpio specifier(s)
*
* The function returns the count of GPIOs specified for a node.
*
@@ -93,14 +94,14 @@ EXPORT_SYMBOL(of_get_named_gpio_flags);
* defines four GPIOs (so this function will return 4), two of which
* are not specified.
*/
-unsigned int of_gpio_count(struct device_node *np)
+unsigned int of_gpio_named_count(struct device_node *np, const char* propname)
{
unsigned int cnt = 0;
do {
int ret;
- ret = of_parse_phandle_with_args(np, "gpios", "#gpio-cells",
+ ret = of_parse_phandle_with_args(np, propname, "#gpio-cells",
cnt, NULL);
/* A hole in the gpios = <> counts anyway. */
if (ret < 0 && ret != -EEXIST)
@@ -109,7 +110,7 @@ unsigned int of_gpio_count(struct device_node *np)
return cnt;
}
-EXPORT_SYMBOL(of_gpio_count);
+EXPORT_SYMBOL(of_gpio_named_count);
/**
* of_gpio_simple_xlate - translate gpio_spec to the GPIO number and flags
@@ -228,7 +229,7 @@ void of_gpiochip_remove(struct gpio_chip *chip)
}
/* Private function for resolving node pointer to gpio_chip */
-static int of_gpiochip_is_match(struct gpio_chip *chip, void *data)
+static int of_gpiochip_is_match(struct gpio_chip *chip, const void *data)
{
return chip->of_node == data;
}
diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c
new file mode 100644
index 0000000..e7cad62
--- /dev/null
+++ b/drivers/of/of_mtd.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * OF helpers for mtd.
+ *
+ * This file is released under the GPLv2
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/of_mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/export.h>
+
+/**
+ * It maps 'enum nand_ecc_modes_t' found in include/linux/mtd/nand.h
+ * into the device tree binding of 'nand-ecc', so that MTD
+ * device driver can get nand ecc from device tree.
+ */
+static const char *nand_ecc_modes[] = {
+ [NAND_ECC_NONE] = "none",
+ [NAND_ECC_SOFT] = "soft",
+ [NAND_ECC_HW] = "hw",
+ [NAND_ECC_HW_SYNDROME] = "hw_syndrome",
+ [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
+ [NAND_ECC_SOFT_BCH] = "soft_bch",
+};
+
+/**
+ * of_get_nand_ecc_mode - Get nand ecc mode for given device_node
+ * @np: Pointer to the given device_node
+ *
+ * The function gets ecc mode string from property 'nand-ecc-mode',
+ * and return its index in nand_ecc_modes table, or errno in error case.
+ */
+const int of_get_nand_ecc_mode(struct device_node *np)
+{
+ const char *pm;
+ int err, i;
+
+ err = of_property_read_string(np, "nand-ecc-mode", &pm);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++)
+ if (!strcasecmp(pm, nand_ecc_modes[i]))
+ return i;
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode);
+
+/**
+ * of_get_nand_bus_width - Get nand bus witdh for given device_node
+ * @np: Pointer to the given device_node
+ *
+ * return bus width option, or errno in error case.
+ */
+int of_get_nand_bus_width(struct device_node *np)
+{
+ u32 val;
+
+ if (of_property_read_u32(np, "nand-bus-width", &val))
+ return 8;
+
+ switch(val) {
+ case 8:
+ case 16:
+ return val;
+ default:
+ return -EIO;
+ }
+}
+EXPORT_SYMBOL_GPL(of_get_nand_bus_width);
+
+/**
+ * of_get_nand_on_flash_bbt - Get nand on flash bbt for given device_node
+ * @np: Pointer to the given device_node
+ *
+ * return true if present false other wise
+ */
+bool of_get_nand_on_flash_bbt(struct device_node *np)
+{
+ return of_property_read_bool(np, "nand-on-flash-bbt");
+}
+EXPORT_SYMBOL_GPL(of_get_nand_on_flash_bbt);
diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c
index 0610e91..432d4bb 100644
--- a/drivers/parisc/dino.c
+++ b/drivers/parisc/dino.c
@@ -55,7 +55,6 @@
#include <asm/pdc.h>
#include <asm/page.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/hardware.h>
diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c
index 95930d0..1f9e9fe 100644
--- a/drivers/parisc/iosapic.c
+++ b/drivers/parisc/iosapic.c
@@ -140,7 +140,6 @@
#include <asm/pdc.h>
#include <asm/pdcpat.h>
#include <asm/page.h>
-#include <asm/system.h>
#include <asm/io.h> /* read/write functions */
#ifdef CONFIG_SUPERIO
#include <asm/superio.h>
diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c
index e885764..052fa23 100644
--- a/drivers/parisc/lba_pci.c
+++ b/drivers/parisc/lba_pci.c
@@ -43,7 +43,6 @@
#include <asm/pdc.h>
#include <asm/pdcpat.h>
#include <asm/page.h>
-#include <asm/system.h>
#include <asm/ropes.h>
#include <asm/hardware.h> /* for register_parisc_driver() stuff */
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 060fd22..0f150f2 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -277,40 +277,6 @@ static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
return 0;
}
-/**
- * acpi_dev_run_wake - Enable/disable wake-up for given device.
- * @phys_dev: Device to enable/disable the platform to wake-up the system for.
- * @enable: Whether enable or disable the wake-up functionality.
- *
- * Find the ACPI device object corresponding to @pci_dev and try to
- * enable/disable the GPE associated with it.
- */
-static int acpi_dev_run_wake(struct device *phys_dev, bool enable)
-{
- struct acpi_device *dev;
- acpi_handle handle;
-
- if (!device_run_wake(phys_dev))
- return -EINVAL;
-
- handle = DEVICE_ACPI_HANDLE(phys_dev);
- if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &dev))) {
- dev_dbg(phys_dev, "ACPI handle has no context in %s!\n",
- __func__);
- return -ENODEV;
- }
-
- if (enable) {
- acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0);
- acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);
- } else {
- acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);
- acpi_disable_wakeup_device_power(dev);
- }
-
- return 0;
-}
-
static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable)
{
while (bus->parent) {
@@ -318,14 +284,14 @@ static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable)
if (bridge->pme_interrupt)
return;
- if (!acpi_dev_run_wake(&bridge->dev, enable))
+ if (!acpi_pm_device_run_wake(&bridge->dev, enable))
return;
bus = bus->parent;
}
/* We have reached the root bus. */
if (bus->bridge)
- acpi_dev_run_wake(bus->bridge, enable);
+ acpi_pm_device_run_wake(bus->bridge, enable);
}
static int acpi_pci_run_wake(struct pci_dev *dev, bool enable)
@@ -333,7 +299,7 @@ static int acpi_pci_run_wake(struct pci_dev *dev, bool enable)
if (dev->pme_interrupt)
return 0;
- if (!acpi_dev_run_wake(&dev->dev, enable))
+ if (!acpi_pm_device_run_wake(&dev->dev, enable))
return 0;
acpi_pci_propagate_run_wake(dev->bus, enable);
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 4bdef24..b500840 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -508,9 +508,6 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
int pos;
u32 reg32;
- if (aspm_disabled)
- return 0;
-
/*
* Some functions in a slot might not all be PCIe functions,
* very strange. Disable ASPM for the whole slot
@@ -519,6 +516,16 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
pos = pci_pcie_cap(child);
if (!pos)
return -EINVAL;
+
+ /*
+ * If ASPM is disabled then we're not going to change
+ * the BIOS state. It's safe to continue even if it's a
+ * pre-1.1 device
+ */
+
+ if (aspm_disabled)
+ continue;
+
/*
* Disable ASPM for pre-1.1 PCIe device, we follow MS to use
* RBER bit to determine if a function is 1.1 version device
diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c
index 1dd68f5..9694c1e 100644
--- a/drivers/pcmcia/at91_cf.c
+++ b/drivers/pcmcia/at91_cf.c
@@ -16,13 +16,13 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/gpio.h>
#include <pcmcia/ss.h>
#include <mach/hardware.h>
#include <asm/io.h>
#include <asm/sizes.h>
-#include <asm/gpio.h>
#include <mach/board.h>
#include <mach/at91rm9200_mc.h>
@@ -70,7 +70,7 @@ static irqreturn_t at91_cf_irq(int irq, void *_cf)
{
struct at91_cf_socket *cf = _cf;
- if (irq == cf->board->det_pin) {
+ if (irq == gpio_to_irq(cf->board->det_pin)) {
unsigned present = at91_cf_present(cf);
/* kick pccard as needed */
@@ -96,8 +96,8 @@ static int at91_cf_get_status(struct pcmcia_socket *s, u_int *sp)
/* NOTE: CF is always 3VCARD */
if (at91_cf_present(cf)) {
- int rdy = cf->board->irq_pin; /* RDY/nIRQ */
- int vcc = cf->board->vcc_pin;
+ int rdy = gpio_is_valid(cf->board->irq_pin); /* RDY/nIRQ */
+ int vcc = gpio_is_valid(cf->board->vcc_pin);
*sp = SS_DETECT | SS_3VCARD;
if (!rdy || gpio_get_value(rdy))
@@ -118,7 +118,7 @@ at91_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
cf = container_of(sock, struct at91_cf_socket, socket);
/* switch Vcc if needed and possible */
- if (cf->board->vcc_pin) {
+ if (gpio_is_valid(cf->board->vcc_pin)) {
switch (s->Vcc) {
case 0:
gpio_set_value(cf->board->vcc_pin, 0);
@@ -222,7 +222,7 @@ static int __init at91_cf_probe(struct platform_device *pdev)
struct resource *io;
int status;
- if (!board || !board->det_pin || !board->rst_pin)
+ if (!board || !gpio_is_valid(board->det_pin) || !gpio_is_valid(board->rst_pin))
return -ENODEV;
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -242,7 +242,7 @@ static int __init at91_cf_probe(struct platform_device *pdev)
status = gpio_request(board->det_pin, "cf_det");
if (status < 0)
goto fail0;
- status = request_irq(board->det_pin, at91_cf_irq, 0, driver_name, cf);
+ status = request_irq(gpio_to_irq(board->det_pin), at91_cf_irq, 0, driver_name, cf);
if (status < 0)
goto fail00;
device_init_wakeup(&pdev->dev, 1);
@@ -251,7 +251,7 @@ static int __init at91_cf_probe(struct platform_device *pdev)
if (status < 0)
goto fail0a;
- if (board->vcc_pin) {
+ if (gpio_is_valid(board->vcc_pin)) {
status = gpio_request(board->vcc_pin, "cf_vcc");
if (status < 0)
goto fail0b;
@@ -263,15 +263,15 @@ static int __init at91_cf_probe(struct platform_device *pdev)
* unless we report that we handle everything (sigh).
* (Note: DK board doesn't wire the IRQ pin...)
*/
- if (board->irq_pin) {
+ if (gpio_is_valid(board->irq_pin)) {
status = gpio_request(board->irq_pin, "cf_irq");
if (status < 0)
goto fail0c;
- status = request_irq(board->irq_pin, at91_cf_irq,
+ status = request_irq(gpio_to_irq(board->irq_pin), at91_cf_irq,
IRQF_SHARED, driver_name, cf);
if (status < 0)
goto fail0d;
- cf->socket.pci_irq = board->irq_pin;
+ cf->socket.pci_irq = gpio_to_irq(board->irq_pin);
} else
cf->socket.pci_irq = nr_irqs + 1;
@@ -290,7 +290,7 @@ static int __init at91_cf_probe(struct platform_device *pdev)
}
pr_info("%s: irqs det #%d, io #%d\n", driver_name,
- board->det_pin, board->irq_pin);
+ gpio_to_irq(board->det_pin), gpio_to_irq(board->irq_pin));
cf->socket.owner = THIS_MODULE;
cf->socket.dev.parent = &pdev->dev;
@@ -312,19 +312,19 @@ fail2:
fail1:
if (cf->socket.io_offset)
iounmap((void __iomem *) cf->socket.io_offset);
- if (board->irq_pin) {
- free_irq(board->irq_pin, cf);
+ if (gpio_is_valid(board->irq_pin)) {
+ free_irq(gpio_to_irq(board->irq_pin), cf);
fail0d:
gpio_free(board->irq_pin);
}
fail0c:
- if (board->vcc_pin)
+ if (gpio_is_valid(board->vcc_pin))
gpio_free(board->vcc_pin);
fail0b:
gpio_free(board->rst_pin);
fail0a:
device_init_wakeup(&pdev->dev, 0);
- free_irq(board->det_pin, cf);
+ free_irq(gpio_to_irq(board->det_pin), cf);
fail00:
gpio_free(board->det_pin);
fail0:
@@ -341,15 +341,15 @@ static int __exit at91_cf_remove(struct platform_device *pdev)
pcmcia_unregister_socket(&cf->socket);
release_mem_region(io->start, resource_size(io));
iounmap((void __iomem *) cf->socket.io_offset);
- if (board->irq_pin) {
- free_irq(board->irq_pin, cf);
+ if (gpio_is_valid(board->irq_pin)) {
+ free_irq(gpio_to_irq(board->irq_pin), cf);
gpio_free(board->irq_pin);
}
- if (board->vcc_pin)
+ if (gpio_is_valid(board->vcc_pin))
gpio_free(board->vcc_pin);
gpio_free(board->rst_pin);
device_init_wakeup(&pdev->dev, 0);
- free_irq(board->det_pin, cf);
+ free_irq(gpio_to_irq(board->det_pin), cf);
gpio_free(board->det_pin);
kfree(cf);
return 0;
@@ -363,9 +363,9 @@ static int at91_cf_suspend(struct platform_device *pdev, pm_message_t mesg)
struct at91_cf_data *board = cf->board;
if (device_may_wakeup(&pdev->dev)) {
- enable_irq_wake(board->det_pin);
- if (board->irq_pin)
- enable_irq_wake(board->irq_pin);
+ enable_irq_wake(gpio_to_irq(board->det_pin));
+ if (gpio_is_valid(board->irq_pin))
+ enable_irq_wake(gpio_to_irq(board->irq_pin));
}
return 0;
}
@@ -376,9 +376,9 @@ static int at91_cf_resume(struct platform_device *pdev)
struct at91_cf_data *board = cf->board;
if (device_may_wakeup(&pdev->dev)) {
- disable_irq_wake(board->det_pin);
- if (board->irq_pin)
- disable_irq_wake(board->irq_pin);
+ disable_irq_wake(gpio_to_irq(board->det_pin));
+ if (gpio_is_valid(board->irq_pin))
+ disable_irq_wake(gpio_to_irq(board->irq_pin));
}
return 0;
diff --git a/drivers/pcmcia/bcm63xx_pcmcia.c b/drivers/pcmcia/bcm63xx_pcmcia.c
index 693577e..c2e997a 100644
--- a/drivers/pcmcia/bcm63xx_pcmcia.c
+++ b/drivers/pcmcia/bcm63xx_pcmcia.c
@@ -475,7 +475,7 @@ static void __devexit bcm63xx_cb_exit(struct pci_dev *dev)
bcm63xx_cb_dev = NULL;
}
-static struct pci_device_id bcm63xx_cb_table[] = {
+static DEFINE_PCI_DEVICE_TABLE(bcm63xx_cb_table) = {
{
.vendor = PCI_VENDOR_ID_BROADCOM,
.device = BCM6348_CPU_ID,
diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c
index 4922139..ac1a223 100644
--- a/drivers/pcmcia/bfin_cf_pcmcia.c
+++ b/drivers/pcmcia/bfin_cf_pcmcia.c
@@ -310,18 +310,7 @@ static struct platform_driver bfin_cf_driver = {
.remove = __devexit_p(bfin_cf_remove),
};
-static int __init bfin_cf_init(void)
-{
- return platform_driver_register(&bfin_cf_driver);
-}
-
-static void __exit bfin_cf_exit(void)
-{
- platform_driver_unregister(&bfin_cf_driver);
-}
-
-module_init(bfin_cf_init);
-module_exit(bfin_cf_exit);
+module_platform_driver(bfin_cf_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("BFIN CF/PCMCIA Driver");
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index d9ea192..673c14e 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -29,7 +29,6 @@
#include <linux/device.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <pcmcia/ss.h>
diff --git a/drivers/pcmcia/db1xxx_ss.c b/drivers/pcmcia/db1xxx_ss.c
index 5b7c227..a484b1f 100644
--- a/drivers/pcmcia/db1xxx_ss.c
+++ b/drivers/pcmcia/db1xxx_ss.c
@@ -172,12 +172,12 @@ static int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock)
if ((sock->board_type == BOARD_TYPE_DB1200) ||
(sock->board_type == BOARD_TYPE_DB1300)) {
ret = request_irq(sock->insert_irq, db1200_pcmcia_cdirq,
- IRQF_DISABLED, "pcmcia_insert", sock);
+ 0, "pcmcia_insert", sock);
if (ret)
goto out1;
ret = request_irq(sock->eject_irq, db1200_pcmcia_cdirq,
- IRQF_DISABLED, "pcmcia_eject", sock);
+ 0, "pcmcia_eject", sock);
if (ret) {
free_irq(sock->insert_irq, sock);
goto out1;
@@ -580,18 +580,7 @@ static struct platform_driver db1x_pcmcia_socket_driver = {
.remove = __devexit_p(db1x_pcmcia_socket_remove),
};
-int __init db1x_pcmcia_socket_load(void)
-{
- return platform_driver_register(&db1x_pcmcia_socket_driver);
-}
-
-void __exit db1x_pcmcia_socket_unload(void)
-{
- platform_driver_unregister(&db1x_pcmcia_socket_driver);
-}
-
-module_init(db1x_pcmcia_socket_load);
-module_exit(db1x_pcmcia_socket_unload);
+module_platform_driver(db1x_pcmcia_socket_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PCMCIA Socket Services for Alchemy Db/Pb1x00 boards");
diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c
index 06ad3e5..7647d23 100644
--- a/drivers/pcmcia/electra_cf.c
+++ b/drivers/pcmcia/electra_cf.c
@@ -365,17 +365,7 @@ static struct platform_driver electra_cf_driver = {
.remove = electra_cf_remove,
};
-static int __init electra_cf_init(void)
-{
- return platform_driver_register(&electra_cf_driver);
-}
-module_init(electra_cf_init);
-
-static void __exit electra_cf_exit(void)
-{
- platform_driver_unregister(&electra_cf_driver);
-}
-module_exit(electra_cf_exit);
+module_platform_driver(electra_cf_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>");
diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c
index 3e447d0..4e8831b 100644
--- a/drivers/pcmcia/i82092.c
+++ b/drivers/pcmcia/i82092.c
@@ -17,7 +17,6 @@
#include <pcmcia/ss.h>
-#include <asm/system.h>
#include <asm/io.h>
#include "i82092aa.h"
@@ -26,14 +25,9 @@
MODULE_LICENSE("GPL");
/* PCI core routines */
-static struct pci_device_id i82092aa_pci_ids[] = {
- {
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_82092AA_0,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- },
- {}
+static DEFINE_PCI_DEVICE_TABLE(i82092aa_pci_ids) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82092AA_0) },
+ { }
};
MODULE_DEVICE_TABLE(pci, i82092aa_pci_ids);
diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c
index 72a033a..e6f3d17d 100644
--- a/drivers/pcmcia/i82365.c
+++ b/drivers/pcmcia/i82365.c
@@ -48,7 +48,6 @@
#include <linux/bitops.h>
#include <asm/irq.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <pcmcia/ss.h>
diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c
index 2adb010..a26f38c 100644
--- a/drivers/pcmcia/m32r_cfc.c
+++ b/drivers/pcmcia/m32r_cfc.c
@@ -24,7 +24,6 @@
#include <linux/bitops.h>
#include <asm/irq.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <pcmcia/ss.h>
diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c
index 1511ff7..2965141 100644
--- a/drivers/pcmcia/m32r_pcc.c
+++ b/drivers/pcmcia/m32r_pcc.c
@@ -24,7 +24,6 @@
#include <linux/bitops.h>
#include <asm/irq.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/addrspace.h>
#include <pcmcia/ss.h>
diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c
index 271a590..a3a851e 100644
--- a/drivers/pcmcia/m8xx_pcmcia.c
+++ b/drivers/pcmcia/m8xx_pcmcia.c
@@ -52,7 +52,6 @@
#include <linux/of_platform.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/time.h>
#include <asm/mpc8xx.h>
#include <asm/8xx_immap.h>
@@ -1304,15 +1303,4 @@ static struct platform_driver m8xx_pcmcia_driver = {
.remove = m8xx_remove,
};
-static int __init m8xx_init(void)
-{
- return platform_driver_register(&m8xx_pcmcia_driver);
-}
-
-static void __exit m8xx_exit(void)
-{
- platform_driver_unregister(&m8xx_pcmcia_driver);
-}
-
-module_init(m8xx_init);
-module_exit(m8xx_exit);
+module_platform_driver(m8xx_pcmcia_driver);
diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c
index 96c72e9..253e386 100644
--- a/drivers/pcmcia/pd6729.c
+++ b/drivers/pcmcia/pd6729.c
@@ -19,7 +19,6 @@
#include <pcmcia/ss.h>
-#include <asm/system.h>
#include "pd6729.h"
#include "i82365.h"
@@ -763,13 +762,8 @@ static void __devexit pd6729_pci_remove(struct pci_dev *dev)
kfree(socket);
}
-static struct pci_device_id pd6729_pci_ids[] = {
- {
- .vendor = PCI_VENDOR_ID_CIRRUS,
- .device = PCI_DEVICE_ID_CIRRUS_6729,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- },
+static DEFINE_PCI_DEVICE_TABLE(pd6729_pci_ids) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6729) },
{ }
};
MODULE_DEVICE_TABLE(pci, pd6729_pci_ids);
diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c
index 66a5422..490bb82 100644
--- a/drivers/pcmcia/pxa2xx_base.c
+++ b/drivers/pcmcia/pxa2xx_base.c
@@ -29,7 +29,6 @@
#include <mach/smemc.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <mach/pxa2xx-regs.h>
#include <asm/mach-types.h>
diff --git a/drivers/pcmcia/pxa2xx_viper.c b/drivers/pcmcia/pxa2xx_viper.c
index adfae49..cb0c37e 100644
--- a/drivers/pcmcia/pxa2xx_viper.c
+++ b/drivers/pcmcia/pxa2xx_viper.c
@@ -177,18 +177,7 @@ static struct platform_driver viper_pcmcia_driver = {
.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_platform_driver(viper_pcmcia_driver);
MODULE_DEVICE_TABLE(platform, viper_pcmcia_id_table);
MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c
index ef5848f..70f728c 100644
--- a/drivers/pcmcia/sa1111_generic.c
+++ b/drivers/pcmcia/sa1111_generic.c
@@ -22,6 +22,40 @@
#include "sa1111_generic.h"
+/*
+ * These are offsets from the above base.
+ */
+#define PCCR 0x0000
+#define PCSSR 0x0004
+#define PCSR 0x0008
+
+#define PCSR_S0_READY (1<<0)
+#define PCSR_S1_READY (1<<1)
+#define PCSR_S0_DETECT (1<<2)
+#define PCSR_S1_DETECT (1<<3)
+#define PCSR_S0_VS1 (1<<4)
+#define PCSR_S0_VS2 (1<<5)
+#define PCSR_S1_VS1 (1<<6)
+#define PCSR_S1_VS2 (1<<7)
+#define PCSR_S0_WP (1<<8)
+#define PCSR_S1_WP (1<<9)
+#define PCSR_S0_BVD1 (1<<10)
+#define PCSR_S0_BVD2 (1<<11)
+#define PCSR_S1_BVD1 (1<<12)
+#define PCSR_S1_BVD2 (1<<13)
+
+#define PCCR_S0_RST (1<<0)
+#define PCCR_S1_RST (1<<1)
+#define PCCR_S0_FLT (1<<2)
+#define PCCR_S1_FLT (1<<3)
+#define PCCR_S0_PWAITEN (1<<4)
+#define PCCR_S1_PWAITEN (1<<5)
+#define PCCR_S0_PSE (1<<6)
+#define PCCR_S1_PSE (1<<7)
+
+#define PCSSR_S0_SLEEP (1<<0)
+#define PCSSR_S1_SLEEP (1<<1)
+
#define IDX_IRQ_S0_READY_NINT (0)
#define IDX_IRQ_S0_CD_VALID (1)
#define IDX_IRQ_S0_BVD1_STSCHG (2)
@@ -32,7 +66,7 @@
void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
struct sa1111_pcmcia_socket *s = to_skt(skt);
- unsigned long status = sa1111_readl(s->dev->mapbase + SA1111_PCSR);
+ unsigned long status = sa1111_readl(s->dev->mapbase + PCSR);
switch (skt->nr) {
case 0:
@@ -88,10 +122,10 @@ int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_s
pccr_set_mask |= PCCR_S0_FLT|PCCR_S1_FLT;
local_irq_save(flags);
- val = sa1111_readl(s->dev->mapbase + SA1111_PCCR);
+ val = sa1111_readl(s->dev->mapbase + PCCR);
val &= ~pccr_skt_mask;
val |= pccr_set_mask & pccr_skt_mask;
- sa1111_writel(val, s->dev->mapbase + SA1111_PCCR);
+ sa1111_writel(val, s->dev->mapbase + PCCR);
local_irq_restore(flags);
return 0;
@@ -141,20 +175,26 @@ int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops,
static int pcmcia_probe(struct sa1111_dev *dev)
{
void __iomem *base;
+ int ret;
+
+ ret = sa1111_enable_device(dev);
+ if (ret)
+ return ret;
dev_set_drvdata(&dev->dev, NULL);
- if (!request_mem_region(dev->res.start, 512,
- SA1111_DRIVER_NAME(dev)))
+ if (!request_mem_region(dev->res.start, 512, SA1111_DRIVER_NAME(dev))) {
+ sa1111_disable_device(dev);
return -EBUSY;
+ }
base = dev->mapbase;
/*
* Initialise the suspend state.
*/
- sa1111_writel(PCSSR_S0_SLEEP | PCSSR_S1_SLEEP, base + SA1111_PCSSR);
- sa1111_writel(PCCR_S0_FLT | PCCR_S1_FLT, base + SA1111_PCCR);
+ sa1111_writel(PCSSR_S0_SLEEP | PCSSR_S1_SLEEP, base + PCSSR);
+ sa1111_writel(PCCR_S0_FLT | PCCR_S1_FLT, base + PCCR);
#ifdef CONFIG_SA1100_BADGE4
pcmcia_badge4_init(&dev->dev);
@@ -184,6 +224,7 @@ static int __devexit pcmcia_remove(struct sa1111_dev *dev)
}
release_mem_region(dev->res.start, 512);
+ sa1111_disable_device(dev);
return 0;
}
diff --git a/drivers/pcmcia/sa1111_neponset.c b/drivers/pcmcia/sa1111_neponset.c
index 50f297d..1d78739 100644
--- a/drivers/pcmcia/sa1111_neponset.c
+++ b/drivers/pcmcia/sa1111_neponset.c
@@ -94,12 +94,7 @@ neponset_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_sta
ret = sa1111_pcmcia_configure_socket(skt, state);
if (ret == 0) {
- unsigned long flags;
-
- local_irq_save(flags);
- NCR_0 = (NCR_0 & ~ncr_mask) | ncr_set;
-
- local_irq_restore(flags);
+ neponset_ncr_frob(ncr_mask, ncr_set);
sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set);
}
diff --git a/drivers/pcmcia/sa11xx_base.c b/drivers/pcmcia/sa11xx_base.c
index a3ee89a..6eecd7c 100644
--- a/drivers/pcmcia/sa11xx_base.c
+++ b/drivers/pcmcia/sa11xx_base.c
@@ -41,7 +41,6 @@
#include <mach/hardware.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include "soc_common.h"
#include "sa11xx_base.h"
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index e0433f5..a2bc6ee 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -46,7 +46,6 @@
#include <linux/timer.h>
#include <mach/hardware.h>
-#include <asm/system.h>
#include "soc_common.h"
diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c
index 71aeed9..d688151 100644
--- a/drivers/pcmcia/socket_sysfs.c
+++ b/drivers/pcmcia/socket_sysfs.c
@@ -23,7 +23,6 @@
#include <linux/pm.h>
#include <linux/device.h>
#include <linux/mutex.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <pcmcia/ss.h>
diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c
index 310160b..cbe15fc 100644
--- a/drivers/pcmcia/tcic.c
+++ b/drivers/pcmcia/tcic.c
@@ -47,7 +47,6 @@
#include <linux/bitops.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <pcmcia/ss.h>
#include "tcic.h"
diff --git a/drivers/pcmcia/vrc4173_cardu.c b/drivers/pcmcia/vrc4173_cardu.c
index c6d36b3..cd0a315 100644
--- a/drivers/pcmcia/vrc4173_cardu.c
+++ b/drivers/pcmcia/vrc4173_cardu.c
@@ -563,11 +563,8 @@ static int __devinit vrc4173_cardu_setup(char *options)
__setup("vrc4173_cardu=", vrc4173_cardu_setup);
-static struct pci_device_id vrc4173_cardu_id_table[] __devinitdata = {
- { .vendor = PCI_VENDOR_ID_NEC,
- .device = PCI_DEVICE_ID_NEC_NAPCCARD,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID, },
+static DEFINE_PCI_DEVICE_TABLE(vrc4173_cardu_id_table) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NAPCCARD) },
{0, }
};
diff --git a/drivers/pcmcia/xxs1500_ss.c b/drivers/pcmcia/xxs1500_ss.c
index 379f421..fd5fbd1 100644
--- a/drivers/pcmcia/xxs1500_ss.c
+++ b/drivers/pcmcia/xxs1500_ss.c
@@ -21,7 +21,6 @@
#include <pcmcia/cistpl.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/mach-au1x00/au1000.h>
#define MEM_MAP_SIZE 0x400000
@@ -321,18 +320,7 @@ static struct platform_driver xxs1500_pcmcia_socket_driver = {
.remove = __devexit_p(xxs1500_pcmcia_remove),
};
-int __init xxs1500_pcmcia_socket_load(void)
-{
- return platform_driver_register(&xxs1500_pcmcia_socket_driver);
-}
-
-void __exit xxs1500_pcmcia_socket_unload(void)
-{
- platform_driver_unregister(&xxs1500_pcmcia_socket_driver);
-}
-
-module_init(xxs1500_pcmcia_socket_load);
-module_exit(xxs1500_pcmcia_socket_unload);
+module_platform_driver(xxs1500_pcmcia_socket_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PCMCIA Socket Services for MyCable XXS1500 systems");
diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c
index 849c0c1..d07f9ac 100644
--- a/drivers/pcmcia/yenta_socket.c
+++ b/drivers/pcmcia/yenta_socket.c
@@ -1352,7 +1352,7 @@ static const struct dev_pm_ops yenta_pm_ops = {
.driver_data = CARDBUS_TYPE_##type, \
}
-static struct pci_device_id yenta_table[] = {
+static DEFINE_PCI_DEVICE_TABLE(yenta_table) = {
CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1031, TI),
/*
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 2dc02c9..2a262f5 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -26,6 +26,10 @@ config ACER_WMI
depends on RFKILL || RFKILL = n
depends on ACPI_WMI
select INPUT_SPARSEKMAP
+ # Acer WMI depends on ACPI_VIDEO when ACPI is enabled
+ # but for select to work, need to select ACPI_VIDEO's dependencies, ick
+ select VIDEO_OUTPUT_CONTROL if ACPI
+ select ACPI_VIDEO if ACPI
---help---
This is a driver for newer Acer (and Wistron) laptops. It adds
wireless radio and bluetooth control, and on some laptops,
@@ -54,7 +58,6 @@ config ACERHDF
config ASUS_LAPTOP
tristate "Asus Laptop Extras"
depends on ACPI
- depends on !ACPI_ASUS
select LEDS_CLASS
select NEW_LEDS
select BACKLIGHT_CLASS_DEVICE
@@ -460,10 +463,9 @@ config INTEL_MENLOW
If unsure, say N.
config EEEPC_LAPTOP
- tristate "Eee PC Hotkey Driver (EXPERIMENTAL)"
+ tristate "Eee PC Hotkey Driver"
depends on ACPI
depends on INPUT
- depends on EXPERIMENTAL
depends on RFKILL || RFKILL = n
depends on HOTPLUG_PCI
select BACKLIGHT_CLASS_DEVICE
@@ -482,11 +484,10 @@ config EEEPC_LAPTOP
doesn't work on your Eee PC, try eeepc-wmi instead.
config ASUS_WMI
- tristate "ASUS WMI Driver (EXPERIMENTAL)"
+ tristate "ASUS WMI Driver"
depends on ACPI_WMI
depends on INPUT
depends on HWMON
- depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL || RFKILL = n
depends on HOTPLUG_PCI
@@ -501,7 +502,7 @@ config ASUS_WMI
be called asus-wmi.
config ASUS_NB_WMI
- tristate "Asus Notebook WMI Driver (EXPERIMENTAL)"
+ tristate "Asus Notebook WMI Driver"
depends on ASUS_WMI
---help---
This is a driver for newer Asus notebooks. It adds extra features
@@ -514,7 +515,7 @@ config ASUS_NB_WMI
here.
config EEEPC_WMI
- tristate "Eee PC WMI Driver (EXPERIMENTAL)"
+ tristate "Eee PC WMI Driver"
depends on ASUS_WMI
---help---
This is a driver for newer Eee PC laptops. It adds extra features
@@ -559,38 +560,6 @@ config MSI_WMI
To compile this driver as a module, choose M here: the module will
be called msi-wmi.
-config ACPI_ASUS
- tristate "ASUS/Medion Laptop Extras (DEPRECATED)"
- depends on ACPI
- select BACKLIGHT_CLASS_DEVICE
- ---help---
- This driver provides support for extra features of ACPI-compatible
- ASUS laptops. As some of Medion laptops are made by ASUS, it may also
- support some Medion laptops (such as 9675 for example). It makes all
- the extra buttons generate standard ACPI events that go through
- /proc/acpi/events, and (on some models) adds support for changing the
- display brightness and output, switching the LCD backlight on and off,
- and most importantly, allows you to blink those fancy LEDs intended
- for reporting mail and wireless status.
-
- Note: display switching code is currently considered EXPERIMENTAL,
- toying with these values may even lock your machine.
-
- All settings are changed via /proc/acpi/asus directory entries. Owner
- and group for these entries can be set with asus_uid and asus_gid
- parameters.
-
- More information and a userspace daemon for handling the extra buttons
- at <http://acpi4asus.sf.net>.
-
- If you have an ACPI-compatible ASUS laptop, say Y or M here. This
- driver is still under development, so if your laptop is unsupported or
- something works not quite as expected, please use the mailing list
- available on the above page (acpi4asus-user@lists.sourceforge.net).
-
- NOTE: This driver is deprecated and will probably be removed soon,
- use asus-laptop instead.
-
config TOPSTAR_LAPTOP
tristate "Topstar Laptop Extras"
depends on ACPI
@@ -604,6 +573,7 @@ config TOPSTAR_LAPTOP
config ACPI_TOSHIBA
tristate "Toshiba Laptop Extras"
depends on ACPI
+ depends on ACPI_WMI
select LEDS_CLASS
select NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
@@ -746,13 +716,18 @@ config XO15_EBOOK
config SAMSUNG_LAPTOP
tristate "Samsung Laptop driver"
- depends on RFKILL && BACKLIGHT_CLASS_DEVICE && X86
+ depends on X86
+ depends on RFKILL || RFKILL = n
+ depends on BACKLIGHT_CLASS_DEVICE
+ select LEDS_CLASS
+ select NEW_LEDS
---help---
This module implements a driver for a wide range of different
Samsung laptops. It offers control over the different
- function keys, wireless LED, LCD backlight level, and
- sometimes provides a "performance_control" sysfs file to allow
- the performance level of the laptop to be changed.
+ function keys, wireless LED, LCD backlight level.
+
+ It may also provide some sysfs files described in
+ <file:Documentation/ABI/testing/sysfs-platform-samsung-laptop>
To compile this driver as a module, choose M here: the module
will be called samsung-laptop.
@@ -781,4 +756,14 @@ config SAMSUNG_Q10
This driver provides support for backlight control on Samsung Q10
and related laptops, including Dell Latitude X200.
+config APPLE_GMUX
+ tristate "Apple Gmux Driver"
+ depends on PNP
+ select BACKLIGHT_CLASS_DEVICE
+ ---help---
+ This driver provides support for the gmux device found on many
+ Apple laptops, which controls the display mux for the hybrid
+ graphics as well as the backlight. Currently only backlight
+ control is supported by the driver.
+
endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index bb94765..bf7e4f9 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -29,9 +29,12 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
obj-$(CONFIG_ACPI_WMI) += wmi.o
obj-$(CONFIG_MSI_WMI) += msi-wmi.o
-obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
+
+# toshiba_acpi must link after wmi to ensure that wmi devices are found
+# before toshiba_acpi initializes
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
+
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
@@ -46,3 +49,4 @@ obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
+obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 1e5290b..c1a3fd8 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -43,6 +43,7 @@
#include <linux/input/sparse-keymap.h>
#include <acpi/acpi_drivers.h>
+#include <acpi/video.h>
MODULE_AUTHOR("Carlos Corbacho");
MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
@@ -105,13 +106,19 @@ static const struct key_entry acer_wmi_keymap[] = {
{KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */
{KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */
{KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */
+ {KE_KEY, 0x29, {KEY_PROG3} }, /* P_Key for TM8372 */
{KE_IGNORE, 0x41, {KEY_MUTE} },
{KE_IGNORE, 0x42, {KEY_PREVIOUSSONG} },
+ {KE_IGNORE, 0x4d, {KEY_PREVIOUSSONG} },
{KE_IGNORE, 0x43, {KEY_NEXTSONG} },
+ {KE_IGNORE, 0x4e, {KEY_NEXTSONG} },
{KE_IGNORE, 0x44, {KEY_PLAYPAUSE} },
+ {KE_IGNORE, 0x4f, {KEY_PLAYPAUSE} },
{KE_IGNORE, 0x45, {KEY_STOP} },
+ {KE_IGNORE, 0x50, {KEY_STOP} },
{KE_IGNORE, 0x48, {KEY_VOLUMEUP} },
{KE_IGNORE, 0x49, {KEY_VOLUMEDOWN} },
+ {KE_IGNORE, 0x4a, {KEY_VOLUMEDOWN} },
{KE_IGNORE, 0x61, {KEY_SWITCHVIDEOMODE} },
{KE_IGNORE, 0x62, {KEY_BRIGHTNESSUP} },
{KE_IGNORE, 0x63, {KEY_BRIGHTNESSDOWN} },
@@ -153,7 +160,14 @@ struct lm_return_value {
u16 reserved;
} __attribute__((packed));
-struct wmid3_gds_input_param { /* Get Device Status input parameter */
+struct wmid3_gds_set_input_param { /* Set Device Status input parameter */
+ u8 function_num; /* Function Number */
+ u8 hotkey_number; /* Hotkey Number */
+ u16 devices; /* Set Device */
+ u8 volume_value; /* Volume Value */
+} __attribute__((packed));
+
+struct wmid3_gds_get_input_param { /* Get Device Status input parameter */
u8 function_num; /* Function Number */
u8 hotkey_number; /* Hotkey Number */
u16 devices; /* Get Device */
@@ -171,6 +185,11 @@ struct hotkey_function_type_aa {
u8 length;
u16 handle;
u16 commun_func_bitmap;
+ u16 application_func_bitmap;
+ u16 media_func_bitmap;
+ u16 display_func_bitmap;
+ u16 others_func_bitmap;
+ u8 commun_fn_key_number;
} __attribute__((packed));
/*
@@ -207,6 +226,7 @@ static int force_series;
static bool ec_raw_mode;
static bool has_type_aa;
static u16 commun_func_bitmap;
+static u8 commun_fn_key_number;
module_param(mailled, int, 0444);
module_param(brightness, int, 0444);
@@ -468,6 +488,15 @@ static struct dmi_system_id acer_quirks[] = {
},
{
.callback = dmi_matched,
+ .ident = "Lenovo Ideapad S205 (Brazos)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Brazos"),
+ },
+ .driver_data = &quirk_lenovo_ideapad_s205,
+ },
+ {
+ .callback = dmi_matched,
.ident = "Lenovo 3000 N200",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
@@ -478,6 +507,25 @@ static struct dmi_system_id acer_quirks[] = {
{}
};
+static int video_set_backlight_video_vendor(const struct dmi_system_id *d)
+{
+ interface->capability &= ~ACER_CAP_BRIGHTNESS;
+ pr_info("Brightness must be controlled by generic video driver\n");
+ return 0;
+}
+
+static const struct dmi_system_id video_vendor_dmi_table[] = {
+ {
+ .callback = video_set_backlight_video_vendor,
+ .ident = "Acer TravelMate 4750",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4750"),
+ },
+ },
+ {}
+};
+
/* Find which quirks are needed for a particular vendor/ model pair */
static void find_quirks(void)
{
@@ -536,8 +584,7 @@ struct acpi_buffer *result)
return status;
}
-static acpi_status AMW0_get_u32(u32 *value, u32 cap,
-struct wmi_interface *iface)
+static acpi_status AMW0_get_u32(u32 *value, u32 cap)
{
int err;
u8 result;
@@ -607,7 +654,7 @@ struct wmi_interface *iface)
return AE_OK;
}
-static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
+static acpi_status AMW0_set_u32(u32 value, u32 cap)
{
struct wmab_args args;
@@ -692,6 +739,7 @@ static const struct acpi_device_id norfkill_ids[] = {
{ "VPC2004", 0},
{ "IBM0068", 0},
{ "LEN0068", 0},
+ { "SNY5001", 0}, /* sony-laptop in charge */
{ "", 0},
};
@@ -827,8 +875,7 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out)
return status;
}
-static acpi_status WMID_get_u32(u32 *value, u32 cap,
-struct wmi_interface *iface)
+static acpi_status WMID_get_u32(u32 *value, u32 cap)
{
acpi_status status;
u8 tmp;
@@ -864,7 +911,7 @@ struct wmi_interface *iface)
return status;
}
-static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
+static acpi_status WMID_set_u32(u32 value, u32 cap)
{
u32 method_id = 0;
char param;
@@ -912,13 +959,13 @@ static acpi_status wmid3_get_device_status(u32 *value, u16 device)
struct wmid3_gds_return_value return_value;
acpi_status status;
union acpi_object *obj;
- struct wmid3_gds_input_param params = {
+ struct wmid3_gds_get_input_param params = {
.function_num = 0x1,
- .hotkey_number = 0x01,
+ .hotkey_number = commun_fn_key_number,
.devices = device,
};
struct acpi_buffer input = {
- sizeof(struct wmid3_gds_input_param),
+ sizeof(struct wmid3_gds_get_input_param),
&params
};
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -981,19 +1028,28 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device)
acpi_status status;
union acpi_object *obj;
u16 devices;
- struct wmid3_gds_input_param params = {
+ struct wmid3_gds_get_input_param get_params = {
.function_num = 0x1,
- .hotkey_number = 0x01,
+ .hotkey_number = commun_fn_key_number,
.devices = commun_func_bitmap,
};
- struct acpi_buffer input = {
- sizeof(struct wmid3_gds_input_param),
- &params
+ struct acpi_buffer get_input = {
+ sizeof(struct wmid3_gds_get_input_param),
+ &get_params
+ };
+ struct wmid3_gds_set_input_param set_params = {
+ .function_num = 0x2,
+ .hotkey_number = commun_fn_key_number,
+ .devices = commun_func_bitmap,
+ };
+ struct acpi_buffer set_input = {
+ sizeof(struct wmid3_gds_set_input_param),
+ &set_params
};
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL };
- status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
+ status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &get_input, &output);
if (ACPI_FAILURE(status))
return status;
@@ -1006,7 +1062,7 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device)
return AE_ERROR;
}
if (obj->buffer.length != 8) {
- pr_warning("Unknown buffer length %d\n", obj->buffer.length);
+ pr_warn("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return AE_ERROR;
}
@@ -1015,18 +1071,16 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device)
kfree(obj);
if (return_value.error_code || return_value.ec_return_value) {
- pr_warning("Get Current Device Status failed: "
- "0x%x - 0x%x\n", return_value.error_code,
+ pr_warn("Get Current Device Status failed: 0x%x - 0x%x\n",
+ return_value.error_code,
return_value.ec_return_value);
return status;
}
devices = return_value.devices;
- params.function_num = 0x2;
- params.hotkey_number = 0x01;
- params.devices = (value) ? (devices | device) : (devices & ~device);
+ set_params.devices = (value) ? (devices | device) : (devices & ~device);
- status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2);
+ status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &set_input, &output2);
if (ACPI_FAILURE(status))
return status;
@@ -1039,7 +1093,7 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device)
return AE_ERROR;
}
if (obj->buffer.length != 4) {
- pr_warning("Unknown buffer length %d\n", obj->buffer.length);
+ pr_warn("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return AE_ERROR;
}
@@ -1048,8 +1102,8 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device)
kfree(obj);
if (return_value.error_code || return_value.ec_return_value)
- pr_warning("Set Device Status failed: "
- "0x%x - 0x%x\n", return_value.error_code,
+ pr_warn("Set Device Status failed: 0x%x - 0x%x\n",
+ return_value.error_code,
return_value.ec_return_value);
return status;
@@ -1096,6 +1150,8 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
interface->capability |= ACER_CAP_THREEG;
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH)
interface->capability |= ACER_CAP_BLUETOOTH;
+
+ commun_fn_key_number = type_aa->commun_fn_key_number;
}
static acpi_status WMID_set_capabilities(void)
@@ -1154,15 +1210,15 @@ static acpi_status get_u32(u32 *value, u32 cap)
switch (interface->type) {
case ACER_AMW0:
- status = AMW0_get_u32(value, cap, interface);
+ status = AMW0_get_u32(value, cap);
break;
case ACER_AMW0_V2:
if (cap == ACER_CAP_MAILLED) {
- status = AMW0_get_u32(value, cap, interface);
+ status = AMW0_get_u32(value, cap);
break;
}
case ACER_WMID:
- status = WMID_get_u32(value, cap, interface);
+ status = WMID_get_u32(value, cap);
break;
case ACER_WMID_v2:
if (cap & (ACER_CAP_WIRELESS |
@@ -1170,7 +1226,7 @@ static acpi_status get_u32(u32 *value, u32 cap)
ACER_CAP_THREEG))
status = wmid_v2_get_u32(value, cap);
else if (wmi_has_guid(WMID_GUID2))
- status = WMID_get_u32(value, cap, interface);
+ status = WMID_get_u32(value, cap);
break;
}
@@ -1184,10 +1240,10 @@ static acpi_status set_u32(u32 value, u32 cap)
if (interface->capability & cap) {
switch (interface->type) {
case ACER_AMW0:
- return AMW0_set_u32(value, cap, interface);
+ return AMW0_set_u32(value, cap);
case ACER_AMW0_V2:
if (cap == ACER_CAP_MAILLED)
- return AMW0_set_u32(value, cap, interface);
+ return AMW0_set_u32(value, cap);
/*
* On some models, some WMID methods don't toggle
@@ -1197,21 +1253,21 @@ static acpi_status set_u32(u32 value, u32 cap)
*/
if (cap == ACER_CAP_WIRELESS ||
cap == ACER_CAP_BLUETOOTH) {
- status = WMID_set_u32(value, cap, interface);
+ status = WMID_set_u32(value, cap);
if (ACPI_FAILURE(status))
return status;
- return AMW0_set_u32(value, cap, interface);
+ return AMW0_set_u32(value, cap);
}
case ACER_WMID:
- return WMID_set_u32(value, cap, interface);
+ return WMID_set_u32(value, cap);
case ACER_WMID_v2:
if (cap & (ACER_CAP_WIRELESS |
ACER_CAP_BLUETOOTH |
ACER_CAP_THREEG))
return wmid_v2_set_u32(value, cap);
else if (wmi_has_guid(WMID_GUID2))
- return WMID_set_u32(value, cap, interface);
+ return WMID_set_u32(value, cap);
default:
return AE_BAD_PARAMETER;
}
@@ -1488,8 +1544,8 @@ static ssize_t show_bool_threeg(struct device *dev,
u32 result; \
acpi_status status;
- pr_info("This threeg sysfs will be removed in 2012"
- " - used by: %s\n", current->comm);
+ pr_info("This threeg sysfs will be removed in 2012 - used by: %s\n",
+ current->comm);
status = get_u32(&result, ACER_CAP_THREEG);
if (ACPI_SUCCESS(status))
return sprintf(buf, "%u\n", result);
@@ -1501,8 +1557,8 @@ static ssize_t set_bool_threeg(struct device *dev,
{
u32 tmp = simple_strtoul(buf, NULL, 10);
acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
- pr_info("This threeg sysfs will be removed in 2012"
- " - used by: %s\n", current->comm);
+ pr_info("This threeg sysfs will be removed in 2012 - used by: %s\n",
+ current->comm);
if (ACPI_FAILURE(status))
return -EINVAL;
return count;
@@ -1513,8 +1569,8 @@ static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg,
static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
char *buf)
{
- pr_info("This interface sysfs will be removed in 2012"
- " - used by: %s\n", current->comm);
+ pr_info("This interface sysfs will be removed in 2012 - used by: %s\n",
+ current->comm);
switch (interface->type) {
case ACER_AMW0:
return sprintf(buf, "AMW0\n");
@@ -1981,9 +2037,13 @@ static int __init acer_wmi_init(void)
set_quirks();
if (acpi_video_backlight_support()) {
- interface->capability &= ~ACER_CAP_BRIGHTNESS;
- pr_info("Brightness must be controlled by "
- "generic video driver\n");
+ if (dmi_check_system(video_vendor_dmi_table)) {
+ acpi_video_unregister();
+ } else {
+ interface->capability &= ~ACER_CAP_BRIGHTNESS;
+ pr_info("Brightness must be controlled by "
+ "acpi video driver\n");
+ }
}
if (wmi_has_guid(WMID_GUID3)) {
@@ -2008,7 +2068,7 @@ static int __init acer_wmi_init(void)
err = platform_driver_register(&acer_platform_driver);
if (err) {
- pr_err("Unable to register platform driver.\n");
+ pr_err("Unable to register platform driver\n");
goto error_platform_register;
}
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index 760c6d7..bc8384c 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -244,12 +244,11 @@ static void acerhdf_change_fanstate(int state)
unsigned char cmd;
if (verbose)
- pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ?
- "OFF" : "ON");
+ pr_notice("fan %s\n", state == ACERHDF_FAN_OFF ? "OFF" : "ON");
if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) {
pr_err("invalid fan state %d requested, setting to auto!\n",
- state);
+ state);
state = ACERHDF_FAN_AUTO;
}
@@ -264,19 +263,18 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal)
{
if (fanon > ACERHDF_MAX_FANON) {
pr_err("fanon temperature too high, set to %d\n",
- ACERHDF_MAX_FANON);
+ ACERHDF_MAX_FANON);
fanon = ACERHDF_MAX_FANON;
}
if (kernelmode && prev_interval != interval) {
if (interval > ACERHDF_MAX_INTERVAL) {
pr_err("interval too high, set to %d\n",
- ACERHDF_MAX_INTERVAL);
+ ACERHDF_MAX_INTERVAL);
interval = ACERHDF_MAX_INTERVAL;
}
if (verbose)
- pr_notice("interval changed to: %d\n",
- interval);
+ pr_notice("interval changed to: %d\n", interval);
thermal->polling_delay = interval*1000;
prev_interval = interval;
}
@@ -587,8 +585,8 @@ static int acerhdf_check_hardware(void)
}
if (!bios_cfg) {
- pr_err("unknown (unsupported) BIOS version %s/%s/%s, "
- "please report, aborting!\n", vendor, product, version);
+ pr_err("unknown (unsupported) BIOS version %s/%s/%s, please report, aborting!\n",
+ vendor, product, version);
return -EINVAL;
}
@@ -598,8 +596,7 @@ static int acerhdf_check_hardware(void)
*/
if (!kernelmode) {
pr_notice("Fan control off, to enable do:\n");
- pr_notice("echo -n \"enabled\" > "
- "/sys/class/thermal/thermal_zone0/mode\n");
+ pr_notice("echo -n \"enabled\" > /sys/class/thermal/thermal_zone0/mode\n");
}
return 0;
diff --git a/drivers/platform/x86/amilo-rfkill.c b/drivers/platform/x86/amilo-rfkill.c
index 19170bb..a514bf6 100644
--- a/drivers/platform/x86/amilo-rfkill.c
+++ b/drivers/platform/x86/amilo-rfkill.c
@@ -97,9 +97,12 @@ static struct rfkill *amilo_rfkill_dev;
static int __devinit amilo_rfkill_probe(struct platform_device *device)
{
+ int rc;
const struct dmi_system_id *system_id =
dmi_first_match(amilo_rfkill_id_table);
- int rc;
+
+ if (!system_id)
+ return -ENXIO;
amilo_rfkill_dev = rfkill_alloc(KBUILD_MODNAME, &device->dev,
RFKILL_TYPE_WLAN,
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
new file mode 100644
index 0000000..8a582bd
--- /dev/null
+++ b/drivers/platform/x86/apple-gmux.c
@@ -0,0 +1,244 @@
+/*
+ * Gmux driver for Apple laptops
+ *
+ * Copyright (C) Canonical Ltd. <seth.forshee@canonical.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/backlight.h>
+#include <linux/acpi.h>
+#include <linux/pnp.h>
+#include <linux/apple_bl.h>
+#include <linux/slab.h>
+#include <acpi/video.h>
+#include <asm/io.h>
+
+struct apple_gmux_data {
+ unsigned long iostart;
+ unsigned long iolen;
+
+ struct backlight_device *bdev;
+};
+
+/*
+ * gmux port offsets. Many of these are not yet used, but may be in the
+ * future, and it's useful to have them documented here anyhow.
+ */
+#define GMUX_PORT_VERSION_MAJOR 0x04
+#define GMUX_PORT_VERSION_MINOR 0x05
+#define GMUX_PORT_VERSION_RELEASE 0x06
+#define GMUX_PORT_SWITCH_DISPLAY 0x10
+#define GMUX_PORT_SWITCH_GET_DISPLAY 0x11
+#define GMUX_PORT_INTERRUPT_ENABLE 0x14
+#define GMUX_PORT_INTERRUPT_STATUS 0x16
+#define GMUX_PORT_SWITCH_DDC 0x28
+#define GMUX_PORT_SWITCH_EXTERNAL 0x40
+#define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41
+#define GMUX_PORT_DISCRETE_POWER 0x50
+#define GMUX_PORT_MAX_BRIGHTNESS 0x70
+#define GMUX_PORT_BRIGHTNESS 0x74
+
+#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
+
+#define GMUX_INTERRUPT_ENABLE 0xff
+#define GMUX_INTERRUPT_DISABLE 0x00
+
+#define GMUX_INTERRUPT_STATUS_ACTIVE 0
+#define GMUX_INTERRUPT_STATUS_DISPLAY (1 << 0)
+#define GMUX_INTERRUPT_STATUS_POWER (1 << 2)
+#define GMUX_INTERRUPT_STATUS_HOTPLUG (1 << 3)
+
+#define GMUX_BRIGHTNESS_MASK 0x00ffffff
+#define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK
+
+static inline u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
+{
+ return inb(gmux_data->iostart + port);
+}
+
+static inline void gmux_write8(struct apple_gmux_data *gmux_data, int port,
+ u8 val)
+{
+ outb(val, gmux_data->iostart + port);
+}
+
+static inline u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
+{
+ return inl(gmux_data->iostart + port);
+}
+
+static int gmux_get_brightness(struct backlight_device *bd)
+{
+ struct apple_gmux_data *gmux_data = bl_get_data(bd);
+ return gmux_read32(gmux_data, GMUX_PORT_BRIGHTNESS) &
+ GMUX_BRIGHTNESS_MASK;
+}
+
+static int gmux_update_status(struct backlight_device *bd)
+{
+ struct apple_gmux_data *gmux_data = bl_get_data(bd);
+ u32 brightness = bd->props.brightness;
+
+ /*
+ * Older gmux versions require writing out lower bytes first then
+ * setting the upper byte to 0 to flush the values. Newer versions
+ * accept a single u32 write, but the old method also works, so we
+ * just use the old method for all gmux versions.
+ */
+ gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
+ gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 1, brightness >> 8);
+ gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 2, brightness >> 16);
+ gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 3, 0);
+
+ return 0;
+}
+
+static const struct backlight_ops gmux_bl_ops = {
+ .get_brightness = gmux_get_brightness,
+ .update_status = gmux_update_status,
+};
+
+static int __devinit gmux_probe(struct pnp_dev *pnp,
+ const struct pnp_device_id *id)
+{
+ struct apple_gmux_data *gmux_data;
+ struct resource *res;
+ struct backlight_properties props;
+ struct backlight_device *bdev;
+ u8 ver_major, ver_minor, ver_release;
+ int ret = -ENXIO;
+
+ gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
+ if (!gmux_data)
+ return -ENOMEM;
+ pnp_set_drvdata(pnp, gmux_data);
+
+ res = pnp_get_resource(pnp, IORESOURCE_IO, 0);
+ if (!res) {
+ pr_err("Failed to find gmux I/O resource\n");
+ goto err_free;
+ }
+
+ gmux_data->iostart = res->start;
+ gmux_data->iolen = res->end - res->start;
+
+ if (gmux_data->iolen < GMUX_MIN_IO_LEN) {
+ pr_err("gmux I/O region too small (%lu < %u)\n",
+ gmux_data->iolen, GMUX_MIN_IO_LEN);
+ goto err_free;
+ }
+
+ if (!request_region(gmux_data->iostart, gmux_data->iolen,
+ "Apple gmux")) {
+ pr_err("gmux I/O already in use\n");
+ goto err_free;
+ }
+
+ /*
+ * On some machines the gmux is in ACPI even thought the machine
+ * doesn't really have a gmux. Check for invalid version information
+ * to detect this.
+ */
+ ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
+ ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
+ ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
+ if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
+ pr_info("gmux device not present\n");
+ ret = -ENODEV;
+ goto err_release;
+ }
+
+ pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor,
+ ver_release);
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS);
+
+ /*
+ * Currently it's assumed that the maximum brightness is less than
+ * 2^24 for compatibility with old gmux versions. Cap the max
+ * brightness at this value, but print a warning if the hardware
+ * reports something higher so that it can be fixed.
+ */
+ if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS))
+ props.max_brightness = GMUX_MAX_BRIGHTNESS;
+
+ bdev = backlight_device_register("gmux_backlight", &pnp->dev,
+ gmux_data, &gmux_bl_ops, &props);
+ if (IS_ERR(bdev)) {
+ ret = PTR_ERR(bdev);
+ goto err_release;
+ }
+
+ gmux_data->bdev = bdev;
+ bdev->props.brightness = gmux_get_brightness(bdev);
+ backlight_update_status(bdev);
+
+ /*
+ * The backlight situation on Macs is complicated. If the gmux is
+ * present it's the best choice, because it always works for
+ * backlight control and supports more levels than other options.
+ * Disable the other backlight choices.
+ */
+ acpi_video_unregister();
+ apple_bl_unregister();
+
+ return 0;
+
+err_release:
+ release_region(gmux_data->iostart, gmux_data->iolen);
+err_free:
+ kfree(gmux_data);
+ return ret;
+}
+
+static void __devexit gmux_remove(struct pnp_dev *pnp)
+{
+ struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+
+ backlight_device_unregister(gmux_data->bdev);
+ release_region(gmux_data->iostart, gmux_data->iolen);
+ kfree(gmux_data);
+
+ acpi_video_register();
+ apple_bl_register();
+}
+
+static const struct pnp_device_id gmux_device_ids[] = {
+ {"APP000B", 0},
+ {"", 0}
+};
+
+static struct pnp_driver gmux_pnp_driver = {
+ .name = "apple-gmux",
+ .probe = gmux_probe,
+ .remove = __devexit_p(gmux_remove),
+ .id_table = gmux_device_ids,
+};
+
+static int __init apple_gmux_init(void)
+{
+ return pnp_register_driver(&gmux_pnp_driver);
+}
+
+static void __exit apple_gmux_exit(void)
+{
+ pnp_unregister_driver(&gmux_pnp_driver);
+}
+
+module_init(apple_gmux_init);
+module_exit(apple_gmux_exit);
+
+MODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>");
+MODULE_DESCRIPTION("Apple Gmux Driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pnp, gmux_device_ids);
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index b7944f9..e38f91b 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -81,6 +81,19 @@ static uint wapf = 1;
module_param(wapf, uint, 0444);
MODULE_PARM_DESC(wapf, "WAPF value");
+static char *wled_type = "unknown";
+static char *bled_type = "unknown";
+
+module_param(wled_type, charp, 0444);
+MODULE_PARM_DESC(wlan_status, "Set the wled type on boot "
+ "(unknown, led or rfkill). "
+ "default is unknown");
+
+module_param(bled_type, charp, 0444);
+MODULE_PARM_DESC(bled_type, "Set the bled type on boot "
+ "(unknown, led or rfkill). "
+ "default is unknown");
+
static int wlan_status = 1;
static int bluetooth_status = 1;
static int wimax_status = -1;
@@ -137,6 +150,11 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot "
#define WM_RSTS 0x08 /* internal wimax */
#define WW_RSTS 0x20 /* internal wwan */
+/* WLED and BLED type */
+#define TYPE_UNKNOWN 0
+#define TYPE_LED 1
+#define TYPE_RFKILL 2
+
/* LED */
#define METHOD_MLED "MLED"
#define METHOD_TLED "TLED"
@@ -218,8 +236,9 @@ struct asus_led {
/*
* Same thing for rfkill
*/
-struct asus_pega_rfkill {
- int control_id; /* type of control. Maps to PEGA_* values */
+struct asus_rfkill {
+ /* type of control. Maps to PEGA_* values or *_RSTS */
+ int control_id;
struct rfkill *rfkill;
struct asus_laptop *asus;
};
@@ -240,6 +259,8 @@ struct asus_laptop {
struct key_entry *keymap;
struct input_polled_dev *pega_accel_poll;
+ struct asus_led wled;
+ struct asus_led bled;
struct asus_led mled;
struct asus_led tled;
struct asus_led rled;
@@ -248,6 +269,8 @@ struct asus_laptop {
struct asus_led kled;
struct workqueue_struct *led_workqueue;
+ int wled_type;
+ int bled_type;
int wireless_status;
bool have_rsts;
bool is_pega_lucid;
@@ -256,11 +279,11 @@ struct asus_laptop {
int pega_acc_y;
int pega_acc_z;
- struct rfkill *gps_rfkill;
-
- struct asus_pega_rfkill wlanrfk;
- struct asus_pega_rfkill btrfk;
- struct asus_pega_rfkill wwanrfk;
+ struct asus_rfkill wlan;
+ struct asus_rfkill bluetooth;
+ struct asus_rfkill wwan;
+ struct asus_rfkill wimax;
+ struct asus_rfkill gps;
acpi_handle handle; /* the handle of the hotk device */
u32 ledd_status; /* status of the LED display */
@@ -274,6 +297,7 @@ static const struct key_entry asus_keymap[] = {
{KE_KEY, 0x02, { KEY_SCREENLOCK } },
{KE_KEY, 0x05, { KEY_WLAN } },
{KE_KEY, 0x08, { KEY_F13 } },
+ {KE_KEY, 0x09, { KEY_PROG2 } }, /* Dock */
{KE_KEY, 0x17, { KEY_ZOOM } },
{KE_KEY, 0x1f, { KEY_BATTERY } },
/* End of Lenovo SL Specific keycodes */
@@ -299,6 +323,8 @@ static const struct key_entry asus_keymap[] = {
{KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } },
{KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } },
{KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */
+ {KE_KEY, 0x6C, { KEY_SLEEP } }, /* Suspend */
+ {KE_KEY, 0x6D, { KEY_SLEEP } }, /* Hibernate */
{KE_KEY, 0x7E, { KEY_BLUETOOTH } },
{KE_KEY, 0x7D, { KEY_BLUETOOTH } },
{KE_KEY, 0x82, { KEY_CAMERA } },
@@ -601,6 +627,10 @@ static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev)
static void asus_led_exit(struct asus_laptop *asus)
{
+ if (!IS_ERR_OR_NULL(asus->wled.led.dev))
+ led_classdev_unregister(&asus->wled.led);
+ if (!IS_ERR_OR_NULL(asus->bled.led.dev))
+ led_classdev_unregister(&asus->bled.led);
if (!IS_ERR_OR_NULL(asus->mled.led.dev))
led_classdev_unregister(&asus->mled.led);
if (!IS_ERR_OR_NULL(asus->tled.led.dev))
@@ -642,7 +672,7 @@ static int asus_led_register(struct asus_laptop *asus,
static int asus_led_init(struct asus_laptop *asus)
{
- int r;
+ int r = 0;
/*
* The Pegatron Lucid has no physical leds, but all methods are
@@ -661,6 +691,16 @@ static int asus_led_init(struct asus_laptop *asus)
if (!asus->led_workqueue)
return -ENOMEM;
+ if (asus->wled_type == TYPE_LED)
+ r = asus_led_register(asus, &asus->wled, "asus::wlan",
+ METHOD_WLAN);
+ if (r)
+ goto error;
+ if (asus->bled_type == TYPE_LED)
+ r = asus_led_register(asus, &asus->bled, "asus::bluetooth",
+ METHOD_BLUETOOTH);
+ if (r)
+ goto error;
r = asus_led_register(asus, &asus->mled, "asus::mail", METHOD_MLED);
if (r)
goto error;
@@ -963,7 +1003,7 @@ static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
return sysfs_acpi_set(asus, buf, count, METHOD_WLAN);
}
-/*
+/*e
* Bluetooth
*/
static int asus_bluetooth_set(struct asus_laptop *asus, int status)
@@ -1228,7 +1268,7 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
ret = asus_gps_switch(asus, !!value);
if (ret)
return ret;
- rfkill_set_sw_state(asus->gps_rfkill, !value);
+ rfkill_set_sw_state(asus->gps.rfkill, !value);
return rv;
}
@@ -1246,90 +1286,139 @@ static const struct rfkill_ops asus_gps_rfkill_ops = {
.set_block = asus_gps_rfkill_set,
};
+static int asus_rfkill_set(void *data, bool blocked)
+{
+ struct asus_rfkill *rfk = data;
+ struct asus_laptop *asus = rfk->asus;
+
+ if (rfk->control_id == WL_RSTS)
+ return asus_wlan_set(asus, !blocked);
+ else if (rfk->control_id == BT_RSTS)
+ return asus_bluetooth_set(asus, !blocked);
+ else if (rfk->control_id == WM_RSTS)
+ return asus_wimax_set(asus, !blocked);
+ else if (rfk->control_id == WW_RSTS)
+ return asus_wwan_set(asus, !blocked);
+
+ return -EINVAL;
+}
+
+static const struct rfkill_ops asus_rfkill_ops = {
+ .set_block = asus_rfkill_set,
+};
+
+static void asus_rfkill_terminate(struct asus_rfkill *rfk)
+{
+ if (!rfk->rfkill)
+ return ;
+
+ rfkill_unregister(rfk->rfkill);
+ rfkill_destroy(rfk->rfkill);
+ rfk->rfkill = NULL;
+}
+
static void asus_rfkill_exit(struct asus_laptop *asus)
{
- if (asus->gps_rfkill) {
- rfkill_unregister(asus->gps_rfkill);
- rfkill_destroy(asus->gps_rfkill);
- asus->gps_rfkill = NULL;
- }
+ asus_rfkill_terminate(&asus->wwan);
+ asus_rfkill_terminate(&asus->bluetooth);
+ asus_rfkill_terminate(&asus->wlan);
+ asus_rfkill_terminate(&asus->gps);
}
-static int asus_rfkill_init(struct asus_laptop *asus)
+static int asus_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *rfk,
+ const char *name, int control_id, int type,
+ const struct rfkill_ops *ops)
{
int result;
- if (acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) ||
- acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) ||
- acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL))
- return 0;
-
- asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev,
- RFKILL_TYPE_GPS,
- &asus_gps_rfkill_ops, asus);
- if (!asus->gps_rfkill)
+ rfk->control_id = control_id;
+ rfk->asus = asus;
+ rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev,
+ type, ops, rfk);
+ if (!rfk->rfkill)
return -EINVAL;
- result = rfkill_register(asus->gps_rfkill);
+ result = rfkill_register(rfk->rfkill);
if (result) {
- rfkill_destroy(asus->gps_rfkill);
- asus->gps_rfkill = NULL;
+ rfkill_destroy(rfk->rfkill);
+ rfk->rfkill = NULL;
}
return result;
}
-static int pega_rfkill_set(void *data, bool blocked)
+static int asus_rfkill_init(struct asus_laptop *asus)
{
- struct asus_pega_rfkill *pega_rfk = data;
+ int result = 0;
- int ret = asus_pega_lucid_set(pega_rfk->asus, pega_rfk->control_id, !blocked);
- pr_warn("Setting rfkill %d, to %d; returned %d\n", pega_rfk->control_id, !blocked, ret);
+ if (asus->is_pega_lucid)
+ return -ENODEV;
- return ret;
-}
+ if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) &&
+ !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) &&
+ !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL))
+ result = asus_rfkill_setup(asus, &asus->gps, "asus-gps",
+ -1, RFKILL_TYPE_GPS,
+ &asus_gps_rfkill_ops);
+ if (result)
+ goto exit;
-static const struct rfkill_ops pega_rfkill_ops = {
- .set_block = pega_rfkill_set,
-};
-static void pega_rfkill_terminate(struct asus_pega_rfkill *pega_rfk)
-{
- pr_warn("Terminating %d\n", pega_rfk->control_id);
- if (pega_rfk->rfkill) {
- rfkill_unregister(pega_rfk->rfkill);
- rfkill_destroy(pega_rfk->rfkill);
- pega_rfk->rfkill = NULL;
- }
-}
+ if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL) &&
+ asus->wled_type == TYPE_RFKILL)
+ result = asus_rfkill_setup(asus, &asus->wlan, "asus-wlan",
+ WL_RSTS, RFKILL_TYPE_WLAN,
+ &asus_rfkill_ops);
+ if (result)
+ goto exit;
-static void pega_rfkill_exit(struct asus_laptop *asus)
-{
- pega_rfkill_terminate(&asus->wwanrfk);
- pega_rfkill_terminate(&asus->btrfk);
- pega_rfkill_terminate(&asus->wlanrfk);
+ if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL) &&
+ asus->bled_type == TYPE_RFKILL)
+ result = asus_rfkill_setup(asus, &asus->bluetooth,
+ "asus-bluetooth", BT_RSTS,
+ RFKILL_TYPE_BLUETOOTH,
+ &asus_rfkill_ops);
+ if (result)
+ goto exit;
+
+ if (!acpi_check_handle(asus->handle, METHOD_WWAN, NULL))
+ result = asus_rfkill_setup(asus, &asus->wwan, "asus-wwan",
+ WW_RSTS, RFKILL_TYPE_WWAN,
+ &asus_rfkill_ops);
+ if (result)
+ goto exit;
+
+ if (!acpi_check_handle(asus->handle, METHOD_WIMAX, NULL))
+ result = asus_rfkill_setup(asus, &asus->wimax, "asus-wimax",
+ WM_RSTS, RFKILL_TYPE_WIMAX,
+ &asus_rfkill_ops);
+ if (result)
+ goto exit;
+
+exit:
+ if (result)
+ asus_rfkill_exit(asus);
+
+ return result;
}
-static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_pega_rfkill *pega_rfk,
- const char *name, int controlid, int rfkill_type)
+static int pega_rfkill_set(void *data, bool blocked)
{
- int result;
+ struct asus_rfkill *rfk = data;
- pr_warn("Setting up rfk %s, control %d, type %d\n", name, controlid, rfkill_type);
- pega_rfk->control_id = controlid;
- pega_rfk->asus = asus;
- pega_rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev,
- rfkill_type, &pega_rfkill_ops, pega_rfk);
- if (!pega_rfk->rfkill)
- return -EINVAL;
+ int ret = asus_pega_lucid_set(rfk->asus, rfk->control_id, !blocked);
+ return ret;
+}
- result = rfkill_register(pega_rfk->rfkill);
- if (result) {
- rfkill_destroy(pega_rfk->rfkill);
- pega_rfk->rfkill = NULL;
- }
+static const struct rfkill_ops pega_rfkill_ops = {
+ .set_block = pega_rfkill_set,
+};
- return result;
+static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *rfk,
+ const char *name, int controlid, int rfkill_type)
+{
+ return asus_rfkill_setup(asus, rfk, name, controlid, rfkill_type,
+ &pega_rfkill_ops);
}
static int pega_rfkill_init(struct asus_laptop *asus)
@@ -1339,22 +1428,22 @@ static int pega_rfkill_init(struct asus_laptop *asus)
if(!asus->is_pega_lucid)
return -ENODEV;
- ret = pega_rfkill_setup(asus, &asus->wlanrfk, "pega-wlan", PEGA_WLAN, RFKILL_TYPE_WLAN);
- if(ret)
- return ret;
- ret = pega_rfkill_setup(asus, &asus->btrfk, "pega-bt", PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH);
+ ret = pega_rfkill_setup(asus, &asus->wlan, "pega-wlan",
+ PEGA_WLAN, RFKILL_TYPE_WLAN);
if(ret)
- goto err_btrfk;
- ret = pega_rfkill_setup(asus, &asus->wwanrfk, "pega-wwan", PEGA_WWAN, RFKILL_TYPE_WWAN);
+ goto exit;
+
+ ret = pega_rfkill_setup(asus, &asus->bluetooth, "pega-bt",
+ PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH);
if(ret)
- goto err_wwanrfk;
+ goto exit;
- pr_warn("Pega rfkill init succeeded\n");
- return 0;
-err_wwanrfk:
- pega_rfkill_terminate(&asus->btrfk);
-err_btrfk:
- pega_rfkill_terminate(&asus->wlanrfk);
+ ret = pega_rfkill_setup(asus, &asus->wwan, "pega-wwan",
+ PEGA_WWAN, RFKILL_TYPE_WWAN);
+
+exit:
+ if (ret)
+ asus_rfkill_exit(asus);
return ret;
}
@@ -1364,8 +1453,10 @@ err_btrfk:
*/
static void asus_input_notify(struct asus_laptop *asus, int event)
{
- if (asus->inputdev)
- sparse_keymap_report_event(asus->inputdev, event, 1, true);
+ if (!asus->inputdev)
+ return ;
+ if (!sparse_keymap_report_event(asus->inputdev, event, 1, true))
+ pr_info("Unknown key %x pressed\n", event);
}
static int asus_input_init(struct asus_laptop *asus)
@@ -1375,7 +1466,7 @@ static int asus_input_init(struct asus_laptop *asus)
input = input_allocate_device();
if (!input) {
- pr_info("Unable to allocate input device\n");
+ pr_warn("Unable to allocate input device\n");
return -ENOMEM;
}
input->name = "Asus Laptop extra buttons";
@@ -1390,7 +1481,7 @@ static int asus_input_init(struct asus_laptop *asus)
}
error = input_register_device(input);
if (error) {
- pr_info("Unable to register input device\n");
+ pr_warn("Unable to register input device\n");
goto err_free_keymap;
}
@@ -1688,7 +1779,16 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
if (result)
return result;
- /* WLED and BLED are on by default */
+ if (!strcmp(bled_type, "led"))
+ asus->bled_type = TYPE_LED;
+ else if (!strcmp(bled_type, "rfkill"))
+ asus->bled_type = TYPE_RFKILL;
+
+ if (!strcmp(wled_type, "led"))
+ asus->wled_type = TYPE_LED;
+ else if (!strcmp(wled_type, "rfkill"))
+ asus->wled_type = TYPE_RFKILL;
+
if (bluetooth_status >= 0)
asus_bluetooth_set(asus, !!bluetooth_status);
@@ -1786,7 +1886,7 @@ static int __devinit asus_acpi_add(struct acpi_device *device)
goto fail_led;
result = asus_rfkill_init(asus);
- if (result)
+ if (result && result != -ENODEV)
goto fail_rfkill;
result = pega_accel_init(asus);
@@ -1828,7 +1928,6 @@ static int asus_acpi_remove(struct acpi_device *device, int type)
asus_led_exit(asus);
asus_input_exit(asus);
pega_accel_exit(asus);
- pega_rfkill_exit(asus);
asus_platform_exit(asus);
kfree(asus->name);
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index b0859d4..99a30b5 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -25,6 +25,7 @@
#include <linux/init.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
+#include <linux/fb.h>
#include "asus-wmi.h"
@@ -51,9 +52,14 @@ static uint wapf;
module_param(wapf, uint, 0444);
MODULE_PARM_DESC(wapf, "WAPF value");
+static struct quirk_entry quirk_asus_unknown = {
+};
+
static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
{
- driver->wapf = wapf;
+ driver->quirks = &quirk_asus_unknown;
+ driver->quirks->wapf = wapf;
+ driver->panel_power = FB_BLANK_UNBLANK;
}
static const struct key_entry asus_nb_wmi_keymap[] = {
@@ -70,6 +76,8 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x50, { KEY_EMAIL } },
{ KE_KEY, 0x51, { KEY_WWW } },
{ KE_KEY, 0x55, { KEY_CALC } },
+ { KE_IGNORE, 0x57, }, /* Battery mode */
+ { KE_IGNORE, 0x58, }, /* AC mode */
{ KE_KEY, 0x5C, { KEY_F15 } }, /* Power Gear key */
{ KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */
{ KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */
@@ -99,7 +107,7 @@ static struct asus_wmi_driver asus_nb_wmi_driver = {
.keymap = asus_nb_wmi_keymap,
.input_name = "Asus WMI hotkeys",
.input_phys = ASUS_NB_WMI_FILE "/input0",
- .quirks = asus_nb_wmi_quirks,
+ .detect_quirks = asus_nb_wmi_quirks,
};
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 9929246..77aadde 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -411,7 +411,7 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
if (retval >= 0) {
if (level)
- *level = retval & 0x80 ? retval & 0x7F : 0;
+ *level = retval & 0x7F;
if (env)
*env = (retval >> 8) & 0x7F;
retval = 0;
@@ -784,7 +784,8 @@ static int asus_new_rfkill(struct asus_wmi *asus,
arfkill->dev_id = dev_id;
arfkill->asus = asus;
- if (dev_id == ASUS_WMI_DEVID_WLAN && asus->driver->hotplug_wireless)
+ if (dev_id == ASUS_WMI_DEVID_WLAN &&
+ asus->driver->quirks->hotplug_wireless)
*rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
&asus_rfkill_wlan_ops, arfkill);
else
@@ -895,7 +896,7 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus)
if (result && result != -ENODEV)
goto exit;
- if (!asus->driver->hotplug_wireless)
+ if (!asus->driver->quirks->hotplug_wireless)
goto exit;
result = asus_setup_pci_hotplug(asus);
@@ -1075,7 +1076,12 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus)
*/
static int read_backlight_power(struct asus_wmi *asus)
{
- int ret = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_BACKLIGHT);
+ int ret;
+ if (asus->driver->quirks->store_backlight_power)
+ ret = !asus->driver->panel_power;
+ else
+ ret = asus_wmi_get_devstate_simple(asus,
+ ASUS_WMI_DEVID_BACKLIGHT);
if (ret < 0)
return ret;
@@ -1116,26 +1122,51 @@ static int read_brightness(struct backlight_device *bd)
return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
}
-static int update_bl_status(struct backlight_device *bd)
+static u32 get_scalar_command(struct backlight_device *bd)
{
struct asus_wmi *asus = bl_get_data(bd);
- u32 ctrl_param;
- int power, err;
+ u32 ctrl_param = 0;
- ctrl_param = bd->props.brightness;
+ if ((asus->driver->brightness < bd->props.brightness) ||
+ bd->props.brightness == bd->props.max_brightness)
+ ctrl_param = 0x00008001;
+ else if ((asus->driver->brightness > bd->props.brightness) ||
+ bd->props.brightness == 0)
+ ctrl_param = 0x00008000;
- err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS,
- ctrl_param, NULL);
+ asus->driver->brightness = bd->props.brightness;
- if (err < 0)
- return err;
+ return ctrl_param;
+}
+
+static int update_bl_status(struct backlight_device *bd)
+{
+ struct asus_wmi *asus = bl_get_data(bd);
+ u32 ctrl_param;
+ int power, err = 0;
power = read_backlight_power(asus);
if (power != -ENODEV && bd->props.power != power) {
ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK);
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT,
ctrl_param, NULL);
+ if (asus->driver->quirks->store_backlight_power)
+ asus->driver->panel_power = bd->props.power;
+
+ /* When using scalar brightness, updating the brightness
+ * will mess with the backlight power */
+ if (asus->driver->quirks->scalar_panel_brightness)
+ return err;
}
+
+ if (asus->driver->quirks->scalar_panel_brightness)
+ ctrl_param = get_scalar_command(bd);
+ else
+ ctrl_param = bd->props.brightness;
+
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS,
+ ctrl_param, NULL);
+
return err;
}
@@ -1196,10 +1227,15 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus)
asus->backlight_device = bd;
+ if (asus->driver->quirks->store_backlight_power)
+ asus->driver->panel_power = power;
+
bd->props.brightness = read_brightness(bd);
bd->props.power = power;
backlight_update_status(bd);
+ asus->driver->brightness = bd->props.brightness;
+
return 0;
}
@@ -1441,9 +1477,9 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
/* CWAP allow to define the behavior of the Fn+F2 key,
* this method doesn't seems to be present on Eee PCs */
- if (asus->driver->wapf >= 0)
+ if (asus->driver->quirks->wapf >= 0)
asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
- asus->driver->wapf, NULL);
+ asus->driver->quirks->wapf, NULL);
return asus_wmi_sysfs_init(asus->platform_device);
}
@@ -1622,8 +1658,8 @@ static int asus_wmi_add(struct platform_device *pdev)
wdrv->platform_device = pdev;
platform_set_drvdata(asus->platform_device, asus);
- if (wdrv->quirks)
- wdrv->quirks(asus->driver);
+ if (wdrv->detect_quirks)
+ wdrv->detect_quirks(asus->driver);
err = asus_wmi_platform_init(asus);
if (err)
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index 8147c10..d43b667 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -35,9 +35,16 @@ struct module;
struct key_entry;
struct asus_wmi;
+struct quirk_entry {
+ bool hotplug_wireless;
+ bool scalar_panel_brightness;
+ bool store_backlight_power;
+ int wapf;
+};
+
struct asus_wmi_driver {
- bool hotplug_wireless;
- int wapf;
+ int brightness;
+ int panel_power;
const char *name;
struct module *owner;
@@ -47,13 +54,14 @@ struct asus_wmi_driver {
const struct key_entry *keymap;
const char *input_name;
const char *input_phys;
+ struct quirk_entry *quirks;
/* Returns new code, value, and autorelease values in arguments.
* Return ASUS_WMI_KEY_IGNORE in code if event should be ignored. */
void (*key_filter) (struct asus_wmi_driver *driver, int *code,
unsigned int *value, bool *autorelease);
int (*probe) (struct platform_device *device);
- void (*quirks) (struct asus_wmi_driver *driver);
+ void (*detect_quirks) (struct asus_wmi_driver *driver);
struct platform_driver platform_driver;
struct platform_device *platform_device;
diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c
deleted file mode 100644
index 6f966d6..0000000
--- a/drivers/platform/x86/asus_acpi.c
+++ /dev/null
@@ -1,1513 +0,0 @@
-/*
- * asus_acpi.c - Asus Laptop ACPI Extras
- *
- *
- * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
- *
- * 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
- *
- *
- * The development page for this driver is located at
- * http://sourceforge.net/projects/acpi4asus/
- *
- * Credits:
- * Pontus Fuchs - Helper functions, cleanup
- * Johann Wiesner - Small compile fixes
- * John Belmonte - ACPI code for Toshiba laptop was a good starting point.
- * �ic Burghard - LED display support for W1N
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/backlight.h>
-#include <acpi/acpi_drivers.h>
-#include <acpi/acpi_bus.h>
-#include <asm/uaccess.h>
-
-#define ASUS_ACPI_VERSION "0.30"
-
-#define PROC_ASUS "asus" /* The directory */
-#define PROC_MLED "mled"
-#define PROC_WLED "wled"
-#define PROC_TLED "tled"
-#define PROC_BT "bluetooth"
-#define PROC_LEDD "ledd"
-#define PROC_INFO "info"
-#define PROC_LCD "lcd"
-#define PROC_BRN "brn"
-#define PROC_DISP "disp"
-
-#define ACPI_HOTK_NAME "Asus Laptop ACPI Extras Driver"
-#define ACPI_HOTK_CLASS "hotkey"
-#define ACPI_HOTK_DEVICE_NAME "Hotkey"
-
-/*
- * Some events we use, same for all Asus
- */
-#define BR_UP 0x10
-#define BR_DOWN 0x20
-
-/*
- * Flags for hotk status
- */
-#define MLED_ON 0x01 /* Mail LED */
-#define WLED_ON 0x02 /* Wireless LED */
-#define TLED_ON 0x04 /* Touchpad LED */
-#define BT_ON 0x08 /* Internal Bluetooth */
-
-MODULE_AUTHOR("Julien Lerouge, Karol Kozimor");
-MODULE_DESCRIPTION(ACPI_HOTK_NAME);
-MODULE_LICENSE("GPL");
-
-static uid_t asus_uid;
-static gid_t asus_gid;
-module_param(asus_uid, uint, 0);
-MODULE_PARM_DESC(asus_uid, "UID for entries in /proc/acpi/asus");
-module_param(asus_gid, uint, 0);
-MODULE_PARM_DESC(asus_gid, "GID for entries in /proc/acpi/asus");
-
-/* For each model, all features implemented,
- * those marked with R are relative to HOTK, A for absolute */
-struct model_data {
- char *name; /* name of the laptop________________A */
- char *mt_mled; /* method to handle mled_____________R */
- char *mled_status; /* node to handle mled reading_______A */
- char *mt_wled; /* method to handle wled_____________R */
- char *wled_status; /* node to handle wled reading_______A */
- char *mt_tled; /* method to handle tled_____________R */
- char *tled_status; /* node to handle tled reading_______A */
- char *mt_ledd; /* method to handle LED display______R */
- char *mt_bt_switch; /* method to switch Bluetooth on/off_R */
- char *bt_status; /* no model currently supports this__? */
- char *mt_lcd_switch; /* method to turn LCD on/off_________A */
- char *lcd_status; /* node to read LCD panel state______A */
- char *brightness_up; /* method to set brightness up_______A */
- char *brightness_down; /* method to set brightness down ____A */
- char *brightness_set; /* method to set absolute brightness_R */
- char *brightness_get; /* method to get absolute brightness_R */
- char *brightness_status;/* node to get brightness____________A */
- char *display_set; /* method to set video output________R */
- char *display_get; /* method to get video output________R */
-};
-
-/*
- * This is the main structure, we can use it to store anything interesting
- * about the hotk device
- */
-struct asus_hotk {
- struct acpi_device *device; /* the device we are in */
- acpi_handle handle; /* the handle of the hotk device */
- char status; /* status of the hotk, for LEDs */
- u32 ledd_status; /* status of the LED display */
- struct model_data *methods; /* methods available on the laptop */
- u8 brightness; /* brightness level */
- enum {
- A1x = 0, /* A1340D, A1300F */
- A2x, /* A2500H */
- A4G, /* A4700G */
- D1x, /* D1 */
- L2D, /* L2000D */
- L3C, /* L3800C */
- L3D, /* L3400D */
- L3H, /* L3H, L2000E, L5D */
- L4R, /* L4500R */
- L5x, /* L5800C */
- L8L, /* L8400L */
- M1A, /* M1300A */
- M2E, /* M2400E, L4400L */
- M6N, /* M6800N, W3400N */
- M6R, /* M6700R, A3000G */
- P30, /* Samsung P30 */
- S1x, /* S1300A, but also L1400B and M2400A (L84F) */
- S2x, /* S200 (J1 reported), Victor MP-XP7210 */
- W1N, /* W1000N */
- W5A, /* W5A */
- W3V, /* W3030V */
- xxN, /* M2400N, M3700N, M5200N, M6800N,
- S1300N, S5200N*/
- A4S, /* Z81sp */
- F3Sa, /* (Centrino) */
- R1F,
- END_MODEL
- } model; /* Models currently supported */
- u16 event_count[128]; /* Count for each event TODO make this better */
-};
-
-/* Here we go */
-#define A1x_PREFIX "\\_SB.PCI0.ISA.EC0."
-#define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0."
-#define M1A_PREFIX "\\_SB.PCI0.PX40.EC0."
-#define P30_PREFIX "\\_SB.PCI0.LPCB.EC0."
-#define S1x_PREFIX "\\_SB.PCI0.PX40."
-#define S2x_PREFIX A1x_PREFIX
-#define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0."
-
-static struct model_data model_conf[END_MODEL] = {
- /*
- * TODO I have seen a SWBX and AIBX method on some models, like L1400B,
- * it seems to be a kind of switch, but what for ?
- */
-
- {
- .name = "A1x",
- .mt_mled = "MLED",
- .mled_status = "\\MAIL",
- .mt_lcd_switch = A1x_PREFIX "_Q10",
- .lcd_status = "\\BKLI",
- .brightness_up = A1x_PREFIX "_Q0E",
- .brightness_down = A1x_PREFIX "_Q0F"},
-
- {
- .name = "A2x",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .wled_status = "\\SG66",
- .mt_lcd_switch = "\\Q10",
- .lcd_status = "\\BAOF",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "A4G",
- .mt_mled = "MLED",
-/* WLED present, but not controlled by ACPI */
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\ADVG"},
-
- {
- .name = "D1x",
- .mt_mled = "MLED",
- .mt_lcd_switch = "\\Q0D",
- .lcd_status = "\\GP11",
- .brightness_up = "\\Q0C",
- .brightness_down = "\\Q0B",
- .brightness_status = "\\BLVL",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "L2D",
- .mt_mled = "MLED",
- .mled_status = "\\SGP6",
- .mt_wled = "WLED",
- .wled_status = "\\RCP3",
- .mt_lcd_switch = "\\Q10",
- .lcd_status = "\\SGP0",
- .brightness_up = "\\Q0E",
- .brightness_down = "\\Q0F",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "L3C",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = L3C_PREFIX "_Q10",
- .lcd_status = "\\GL32",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\_SB.PCI0.PCI1.VGAC.NMAP"},
-
- {
- .name = "L3D",
- .mt_mled = "MLED",
- .mled_status = "\\MALD",
- .mt_wled = "WLED",
- .mt_lcd_switch = "\\Q10",
- .lcd_status = "\\BKLG",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "L3H",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = "EHK",
- .lcd_status = "\\_SB.PCI0.PM.PBC",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "L4R",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .wled_status = "\\_SB.PCI0.SBRG.SG13",
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"},
-
- {
- .name = "L5x",
- .mt_mled = "MLED",
-/* WLED present, but not controlled by ACPI */
- .mt_tled = "TLED",
- .mt_lcd_switch = "\\Q0D",
- .lcd_status = "\\BAOF",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "L8L"
-/* No features, but at least support the hotkeys */
- },
-
- {
- .name = "M1A",
- .mt_mled = "MLED",
- .mt_lcd_switch = M1A_PREFIX "Q10",
- .lcd_status = "\\PNOF",
- .brightness_up = M1A_PREFIX "Q0E",
- .brightness_down = M1A_PREFIX "Q0F",
- .brightness_status = "\\BRIT",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "M2E",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = "\\Q10",
- .lcd_status = "\\GP06",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "M6N",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .wled_status = "\\_SB.PCI0.SBRG.SG13",
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .lcd_status = "\\_SB.BKLT",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\SSTE"},
-
- {
- .name = "M6R",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"},
-
- {
- .name = "P30",
- .mt_wled = "WLED",
- .mt_lcd_switch = P30_PREFIX "_Q0E",
- .lcd_status = "\\BKLT",
- .brightness_up = P30_PREFIX "_Q68",
- .brightness_down = P30_PREFIX "_Q69",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\DNXT"},
-
- {
- .name = "S1x",
- .mt_mled = "MLED",
- .mled_status = "\\EMLE",
- .mt_wled = "WLED",
- .mt_lcd_switch = S1x_PREFIX "Q10",
- .lcd_status = "\\PNOF",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV"},
-
- {
- .name = "S2x",
- .mt_mled = "MLED",
- .mled_status = "\\MAIL",
- .mt_lcd_switch = S2x_PREFIX "_Q10",
- .lcd_status = "\\BKLI",
- .brightness_up = S2x_PREFIX "_Q0B",
- .brightness_down = S2x_PREFIX "_Q0A"},
-
- {
- .name = "W1N",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_ledd = "SLCM",
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .lcd_status = "\\BKLT",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\ADVG"},
-
- {
- .name = "W5A",
- .mt_bt_switch = "BLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\ADVG"},
-
- {
- .name = "W3V",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .lcd_status = "\\BKLT",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "xxN",
- .mt_mled = "MLED",
-/* WLED present, but not controlled by ACPI */
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .lcd_status = "\\BKLT",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\ADVG"},
-
- {
- .name = "A4S",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .mt_bt_switch = "BLED",
- .mt_wled = "WLED"
- },
-
- {
- .name = "F3Sa",
- .mt_bt_switch = "BLED",
- .mt_wled = "WLED",
- .mt_mled = "MLED",
- .brightness_get = "GPLV",
- .brightness_set = "SPLV",
- .mt_lcd_switch = "\\_SB.PCI0.SBRG.EC0._Q10",
- .lcd_status = "\\_SB.PCI0.SBRG.EC0.RPIN",
- .display_get = "\\ADVG",
- .display_set = "SDSP",
- },
- {
- .name = "R1F",
- .mt_bt_switch = "BLED",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = "\\Q10",
- .lcd_status = "\\GP06",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"
- }
-};
-
-/* procdir we use */
-static struct proc_dir_entry *asus_proc_dir;
-
-static struct backlight_device *asus_backlight_device;
-
-/*
- * This header is made available to allow proper configuration given model,
- * revision number , ... this info cannot go in struct asus_hotk because it is
- * available before the hotk
- */
-static struct acpi_table_header *asus_info;
-
-/* The actual device the driver binds to */
-static struct asus_hotk *hotk;
-
-/*
- * The hotkey driver and autoloading declaration
- */
-static int asus_hotk_add(struct acpi_device *device);
-static int asus_hotk_remove(struct acpi_device *device, int type);
-static void asus_hotk_notify(struct acpi_device *device, u32 event);
-
-static const struct acpi_device_id asus_device_ids[] = {
- {"ATK0100", 0},
- {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, asus_device_ids);
-
-static struct acpi_driver asus_hotk_driver = {
- .name = "asus_acpi",
- .class = ACPI_HOTK_CLASS,
- .owner = THIS_MODULE,
- .ids = asus_device_ids,
- .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
- .ops = {
- .add = asus_hotk_add,
- .remove = asus_hotk_remove,
- .notify = asus_hotk_notify,
- },
-};
-
-/*
- * This function evaluates an ACPI method, given an int as parameter, the
- * method is searched within the scope of the handle, can be NULL. The output
- * of the method is written is output, which can also be NULL
- *
- * returns 1 if write is successful, 0 else.
- */
-static int write_acpi_int(acpi_handle handle, const char *method, int val,
- struct acpi_buffer *output)
-{
- struct acpi_object_list params; /* list of input parameters (int) */
- union acpi_object in_obj; /* the only param we use */
- acpi_status status;
-
- params.count = 1;
- params.pointer = &in_obj;
- in_obj.type = ACPI_TYPE_INTEGER;
- in_obj.integer.value = val;
-
- status = acpi_evaluate_object(handle, (char *)method, &params, output);
- return (status == AE_OK);
-}
-
-static int read_acpi_int(acpi_handle handle, const char *method, int *val)
-{
- struct acpi_buffer output;
- union acpi_object out_obj;
- acpi_status status;
-
- output.length = sizeof(out_obj);
- output.pointer = &out_obj;
-
- status = acpi_evaluate_object(handle, (char *)method, NULL, &output);
- *val = out_obj.integer.value;
- return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
-}
-
-static int asus_info_proc_show(struct seq_file *m, void *v)
-{
- int temp;
-
- seq_printf(m, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n");
- seq_printf(m, "Model reference : %s\n", hotk->methods->name);
- /*
- * The SFUN method probably allows the original driver to get the list
- * of features supported by a given model. For now, 0x0100 or 0x0800
- * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
- * The significance of others is yet to be found.
- */
- if (read_acpi_int(hotk->handle, "SFUN", &temp))
- seq_printf(m, "SFUN value : 0x%04x\n", temp);
- /*
- * Another value for userspace: the ASYM method returns 0x02 for
- * battery low and 0x04 for battery critical, its readings tend to be
- * more accurate than those provided by _BST.
- * Note: since not all the laptops provide this method, errors are
- * silently ignored.
- */
- if (read_acpi_int(hotk->handle, "ASYM", &temp))
- seq_printf(m, "ASYM value : 0x%04x\n", temp);
- if (asus_info) {
- seq_printf(m, "DSDT length : %d\n", asus_info->length);
- seq_printf(m, "DSDT checksum : %d\n", asus_info->checksum);
- seq_printf(m, "DSDT revision : %d\n", asus_info->revision);
- seq_printf(m, "OEM id : %.*s\n", ACPI_OEM_ID_SIZE, asus_info->oem_id);
- seq_printf(m, "OEM table id : %.*s\n", ACPI_OEM_TABLE_ID_SIZE, asus_info->oem_table_id);
- seq_printf(m, "OEM revision : 0x%x\n", asus_info->oem_revision);
- seq_printf(m, "ASL comp vendor id : %.*s\n", ACPI_NAME_SIZE, asus_info->asl_compiler_id);
- seq_printf(m, "ASL comp revision : 0x%x\n", asus_info->asl_compiler_revision);
- }
-
- return 0;
-}
-
-static int asus_info_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, asus_info_proc_show, NULL);
-}
-
-static const struct file_operations asus_info_proc_fops = {
- .owner = THIS_MODULE,
- .open = asus_info_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-/*
- * /proc handlers
- * We write our info in page, we begin at offset off and cannot write more
- * than count bytes. We set eof to 1 if we handle those 2 values. We return the
- * number of bytes written in page
- */
-
-/* Generic LED functions */
-static int read_led(const char *ledname, int ledmask)
-{
- if (ledname) {
- int led_status;
-
- if (read_acpi_int(NULL, ledname, &led_status))
- return led_status;
- else
- pr_warn("Error reading LED status\n");
- }
- return (hotk->status & ledmask) ? 1 : 0;
-}
-
-static int parse_arg(const char __user *buf, unsigned long count, int *val)
-{
- char s[32];
- if (!count)
- return 0;
- if (count > 31)
- return -EINVAL;
- if (copy_from_user(s, buf, count))
- return -EFAULT;
- s[count] = 0;
- if (sscanf(s, "%i", val) != 1)
- return -EINVAL;
- return count;
-}
-
-/* FIXME: kill extraneous args so it can be called independently */
-static int
-write_led(const char __user *buffer, unsigned long count,
- char *ledname, int ledmask, int invert)
-{
- int rv, value;
- int led_out = 0;
-
- rv = parse_arg(buffer, count, &value);
- if (rv > 0)
- led_out = value ? 1 : 0;
-
- hotk->status =
- (led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask);
-
- if (invert) /* invert target value */
- led_out = !led_out;
-
- if (!write_acpi_int(hotk->handle, ledname, led_out, NULL))
- pr_warn("LED (%s) write failed\n", ledname);
-
- return rv;
-}
-
-/*
- * Proc handlers for MLED
- */
-static int mled_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", read_led(hotk->methods->mled_status, MLED_ON));
- return 0;
-}
-
-static int mled_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, mled_proc_show, NULL);
-}
-
-static ssize_t mled_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- return write_led(buffer, count, hotk->methods->mt_mled, MLED_ON, 1);
-}
-
-static const struct file_operations mled_proc_fops = {
- .owner = THIS_MODULE,
- .open = mled_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = mled_proc_write,
-};
-
-/*
- * Proc handlers for LED display
- */
-static int ledd_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "0x%08x\n", hotk->ledd_status);
- return 0;
-}
-
-static int ledd_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ledd_proc_show, NULL);
-}
-
-static ssize_t ledd_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- int rv, value;
-
- rv = parse_arg(buffer, count, &value);
- if (rv > 0) {
- if (!write_acpi_int
- (hotk->handle, hotk->methods->mt_ledd, value, NULL))
- pr_warn("LED display write failed\n");
- else
- hotk->ledd_status = (u32) value;
- }
- return rv;
-}
-
-static const struct file_operations ledd_proc_fops = {
- .owner = THIS_MODULE,
- .open = ledd_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = ledd_proc_write,
-};
-
-/*
- * Proc handlers for WLED
- */
-static int wled_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", read_led(hotk->methods->wled_status, WLED_ON));
- return 0;
-}
-
-static int wled_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, wled_proc_show, NULL);
-}
-
-static ssize_t wled_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0);
-}
-
-static const struct file_operations wled_proc_fops = {
- .owner = THIS_MODULE,
- .open = wled_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = wled_proc_write,
-};
-
-/*
- * Proc handlers for Bluetooth
- */
-static int bluetooth_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", read_led(hotk->methods->bt_status, BT_ON));
- return 0;
-}
-
-static int bluetooth_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, bluetooth_proc_show, NULL);
-}
-
-static ssize_t bluetooth_proc_write(struct file *file,
- const char __user *buffer, size_t count, loff_t *pos)
-{
- /* Note: mt_bt_switch controls both internal Bluetooth adapter's
- presence and its LED */
- return write_led(buffer, count, hotk->methods->mt_bt_switch, BT_ON, 0);
-}
-
-static const struct file_operations bluetooth_proc_fops = {
- .owner = THIS_MODULE,
- .open = bluetooth_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = bluetooth_proc_write,
-};
-
-/*
- * Proc handlers for TLED
- */
-static int tled_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", read_led(hotk->methods->tled_status, TLED_ON));
- return 0;
-}
-
-static int tled_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, tled_proc_show, NULL);
-}
-
-static ssize_t tled_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- return write_led(buffer, count, hotk->methods->mt_tled, TLED_ON, 0);
-}
-
-static const struct file_operations tled_proc_fops = {
- .owner = THIS_MODULE,
- .open = tled_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = tled_proc_write,
-};
-
-static int get_lcd_state(void)
-{
- int lcd = 0;
-
- if (hotk->model == L3H) {
- /* L3H and the like have to be handled differently */
- acpi_status status = 0;
- struct acpi_object_list input;
- union acpi_object mt_params[2];
- struct acpi_buffer output;
- union acpi_object out_obj;
-
- input.count = 2;
- input.pointer = mt_params;
- /* Note: the following values are partly guessed up, but
- otherwise they seem to work */
- mt_params[0].type = ACPI_TYPE_INTEGER;
- mt_params[0].integer.value = 0x02;
- mt_params[1].type = ACPI_TYPE_INTEGER;
- mt_params[1].integer.value = 0x02;
-
- output.length = sizeof(out_obj);
- output.pointer = &out_obj;
-
- status =
- acpi_evaluate_object(NULL, hotk->methods->lcd_status,
- &input, &output);
- if (status != AE_OK)
- return -1;
- if (out_obj.type == ACPI_TYPE_INTEGER)
- /* That's what the AML code does */
- lcd = out_obj.integer.value >> 8;
- } else if (hotk->model == F3Sa) {
- unsigned long long tmp;
- union acpi_object param;
- struct acpi_object_list input;
- acpi_status status;
-
- /* Read pin 11 */
- param.type = ACPI_TYPE_INTEGER;
- param.integer.value = 0x11;
- input.count = 1;
- input.pointer = &param;
-
- status = acpi_evaluate_integer(NULL, hotk->methods->lcd_status,
- &input, &tmp);
- if (status != AE_OK)
- return -1;
-
- lcd = tmp;
- } else {
- /* We don't have to check anything if we are here */
- if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd))
- pr_warn("Error reading LCD status\n");
-
- if (hotk->model == L2D)
- lcd = ~lcd;
- }
-
- return (lcd & 1);
-}
-
-static int set_lcd_state(int value)
-{
- int lcd = 0;
- acpi_status status = 0;
-
- lcd = value ? 1 : 0;
- if (lcd != get_lcd_state()) {
- /* switch */
- if (hotk->model != L3H) {
- status =
- acpi_evaluate_object(NULL,
- hotk->methods->mt_lcd_switch,
- NULL, NULL);
- } else {
- /* L3H and the like must be handled differently */
- if (!write_acpi_int
- (hotk->handle, hotk->methods->mt_lcd_switch, 0x07,
- NULL))
- status = AE_ERROR;
- /* L3H's AML executes EHK (0x07) upon Fn+F7 keypress,
- the exact behaviour is simulated here */
- }
- if (ACPI_FAILURE(status))
- pr_warn("Error switching LCD\n");
- }
- return 0;
-
-}
-
-static int lcd_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", get_lcd_state());
- return 0;
-}
-
-static int lcd_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, lcd_proc_show, NULL);
-}
-
-static ssize_t lcd_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- int rv, value;
-
- rv = parse_arg(buffer, count, &value);
- if (rv > 0)
- set_lcd_state(value);
- return rv;
-}
-
-static const struct file_operations lcd_proc_fops = {
- .owner = THIS_MODULE,
- .open = lcd_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = lcd_proc_write,
-};
-
-static int read_brightness(struct backlight_device *bd)
-{
- int value;
-
- if (hotk->methods->brightness_get) { /* SPLV/GPLV laptop */
- if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get,
- &value))
- pr_warn("Error reading brightness\n");
- } else if (hotk->methods->brightness_status) { /* For D1 for example */
- if (!read_acpi_int(NULL, hotk->methods->brightness_status,
- &value))
- pr_warn("Error reading brightness\n");
- } else /* No GPLV method */
- value = hotk->brightness;
- return value;
-}
-
-/*
- * Change the brightness level
- */
-static int set_brightness(int value)
-{
- acpi_status status = 0;
- int ret = 0;
-
- /* SPLV laptop */
- if (hotk->methods->brightness_set) {
- if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set,
- value, NULL)) {
- pr_warn("Error changing brightness\n");
- ret = -EIO;
- }
- goto out;
- }
-
- /* No SPLV method if we are here, act as appropriate */
- value -= read_brightness(NULL);
- while (value != 0) {
- status = acpi_evaluate_object(NULL, (value > 0) ?
- hotk->methods->brightness_up :
- hotk->methods->brightness_down,
- NULL, NULL);
- (value > 0) ? value-- : value++;
- if (ACPI_FAILURE(status)) {
- pr_warn("Error changing brightness\n");
- ret = -EIO;
- }
- }
-out:
- return ret;
-}
-
-static int set_brightness_status(struct backlight_device *bd)
-{
- return set_brightness(bd->props.brightness);
-}
-
-static int brn_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", read_brightness(NULL));
- return 0;
-}
-
-static int brn_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, brn_proc_show, NULL);
-}
-
-static ssize_t brn_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- int rv, value;
-
- rv = parse_arg(buffer, count, &value);
- if (rv > 0) {
- value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
- /* 0 <= value <= 15 */
- set_brightness(value);
- }
- return rv;
-}
-
-static const struct file_operations brn_proc_fops = {
- .owner = THIS_MODULE,
- .open = brn_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = brn_proc_write,
-};
-
-static void set_display(int value)
-{
- /* no sanity check needed for now */
- if (!write_acpi_int(hotk->handle, hotk->methods->display_set,
- value, NULL))
- pr_warn("Error setting display\n");
- return;
-}
-
-/*
- * Now, *this* one could be more user-friendly, but so far, no-one has
- * complained. The significance of bits is the same as in proc_write_disp()
- */
-static int disp_proc_show(struct seq_file *m, void *v)
-{
- int value = 0;
-
- if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value))
- pr_warn("Error reading display status\n");
- value &= 0x07; /* needed for some models, shouldn't hurt others */
- seq_printf(m, "%d\n", value);
- return 0;
-}
-
-static int disp_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, disp_proc_show, NULL);
-}
-
-/*
- * Experimental support for display switching. As of now: 1 should activate
- * the LCD output, 2 should do for CRT, and 4 for TV-Out. Any combination
- * (bitwise) of these will suffice. I never actually tested 3 displays hooked
- * up simultaneously, so be warned. See the acpi4asus README for more info.
- */
-static ssize_t disp_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- int rv, value;
-
- rv = parse_arg(buffer, count, &value);
- if (rv > 0)
- set_display(value);
- return rv;
-}
-
-static const struct file_operations disp_proc_fops = {
- .owner = THIS_MODULE,
- .open = disp_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = disp_proc_write,
-};
-
-static int
-asus_proc_add(char *name, const struct file_operations *proc_fops, umode_t mode,
- struct acpi_device *device)
-{
- struct proc_dir_entry *proc;
-
- proc = proc_create_data(name, mode, acpi_device_dir(device),
- proc_fops, acpi_driver_data(device));
- if (!proc) {
- pr_warn(" Unable to create %s fs entry\n", name);
- return -1;
- }
- proc->uid = asus_uid;
- proc->gid = asus_gid;
- return 0;
-}
-
-static int asus_hotk_add_fs(struct acpi_device *device)
-{
- struct proc_dir_entry *proc;
- umode_t mode;
-
- if ((asus_uid == 0) && (asus_gid == 0)) {
- mode = S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP;
- } else {
- mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP;
- pr_warn(" asus_uid and asus_gid parameters are "
- "deprecated, use chown and chmod instead!\n");
- }
-
- acpi_device_dir(device) = asus_proc_dir;
- if (!acpi_device_dir(device))
- return -ENODEV;
-
- proc = proc_create(PROC_INFO, mode, acpi_device_dir(device),
- &asus_info_proc_fops);
- if (proc) {
- proc->uid = asus_uid;
- proc->gid = asus_gid;
- } else {
- pr_warn(" Unable to create " PROC_INFO " fs entry\n");
- }
-
- if (hotk->methods->mt_wled) {
- asus_proc_add(PROC_WLED, &wled_proc_fops, mode, device);
- }
-
- if (hotk->methods->mt_ledd) {
- asus_proc_add(PROC_LEDD, &ledd_proc_fops, mode, device);
- }
-
- if (hotk->methods->mt_mled) {
- asus_proc_add(PROC_MLED, &mled_proc_fops, mode, device);
- }
-
- if (hotk->methods->mt_tled) {
- asus_proc_add(PROC_TLED, &tled_proc_fops, mode, device);
- }
-
- if (hotk->methods->mt_bt_switch) {
- asus_proc_add(PROC_BT, &bluetooth_proc_fops, mode, device);
- }
-
- /*
- * We need both read node and write method as LCD switch is also
- * accessible from the keyboard
- */
- if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) {
- asus_proc_add(PROC_LCD, &lcd_proc_fops, mode, device);
- }
-
- if ((hotk->methods->brightness_up && hotk->methods->brightness_down) ||
- (hotk->methods->brightness_get && hotk->methods->brightness_set)) {
- asus_proc_add(PROC_BRN, &brn_proc_fops, mode, device);
- }
-
- if (hotk->methods->display_set) {
- asus_proc_add(PROC_DISP, &disp_proc_fops, mode, device);
- }
-
- return 0;
-}
-
-static int asus_hotk_remove_fs(struct acpi_device *device)
-{
- if (acpi_device_dir(device)) {
- remove_proc_entry(PROC_INFO, acpi_device_dir(device));
- if (hotk->methods->mt_wled)
- remove_proc_entry(PROC_WLED, acpi_device_dir(device));
- if (hotk->methods->mt_mled)
- remove_proc_entry(PROC_MLED, acpi_device_dir(device));
- if (hotk->methods->mt_tled)
- remove_proc_entry(PROC_TLED, acpi_device_dir(device));
- if (hotk->methods->mt_ledd)
- remove_proc_entry(PROC_LEDD, acpi_device_dir(device));
- if (hotk->methods->mt_bt_switch)
- remove_proc_entry(PROC_BT, acpi_device_dir(device));
- if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status)
- remove_proc_entry(PROC_LCD, acpi_device_dir(device));
- if ((hotk->methods->brightness_up
- && hotk->methods->brightness_down)
- || (hotk->methods->brightness_get
- && hotk->methods->brightness_set))
- remove_proc_entry(PROC_BRN, acpi_device_dir(device));
- if (hotk->methods->display_set)
- remove_proc_entry(PROC_DISP, acpi_device_dir(device));
- }
- return 0;
-}
-
-static void asus_hotk_notify(struct acpi_device *device, u32 event)
-{
- /* TODO Find a better way to handle events count. */
- if (!hotk)
- return;
-
- /*
- * The BIOS *should* be sending us device events, but apparently
- * Asus uses system events instead, so just ignore any device
- * events we get.
- */
- if (event > ACPI_MAX_SYS_NOTIFY)
- return;
-
- if ((event & ~((u32) BR_UP)) < 16)
- hotk->brightness = (event & ~((u32) BR_UP));
- else if ((event & ~((u32) BR_DOWN)) < 16)
- hotk->brightness = (event & ~((u32) BR_DOWN));
-
- acpi_bus_generate_proc_event(hotk->device, event,
- hotk->event_count[event % 128]++);
-
- return;
-}
-
-/*
- * Match the model string to the list of supported models. Return END_MODEL if
- * no match or model is NULL.
- */
-static int asus_model_match(char *model)
-{
- if (model == NULL)
- return END_MODEL;
-
- if (strncmp(model, "L3D", 3) == 0)
- return L3D;
- else if (strncmp(model, "L2E", 3) == 0 ||
- strncmp(model, "L3H", 3) == 0 || strncmp(model, "L5D", 3) == 0)
- return L3H;
- else if (strncmp(model, "L3", 2) == 0 || strncmp(model, "L2B", 3) == 0)
- return L3C;
- else if (strncmp(model, "L8L", 3) == 0)
- return L8L;
- else if (strncmp(model, "L4R", 3) == 0)
- return L4R;
- else if (strncmp(model, "M6N", 3) == 0 || strncmp(model, "W3N", 3) == 0)
- return M6N;
- else if (strncmp(model, "M6R", 3) == 0 || strncmp(model, "A3G", 3) == 0)
- return M6R;
- else if (strncmp(model, "M2N", 3) == 0 ||
- strncmp(model, "M3N", 3) == 0 ||
- strncmp(model, "M5N", 3) == 0 ||
- strncmp(model, "S1N", 3) == 0 ||
- strncmp(model, "S5N", 3) == 0)
- return xxN;
- else if (strncmp(model, "M1", 2) == 0)
- return M1A;
- else if (strncmp(model, "M2", 2) == 0 || strncmp(model, "L4E", 3) == 0)
- return M2E;
- else if (strncmp(model, "L2", 2) == 0)
- return L2D;
- else if (strncmp(model, "L8", 2) == 0)
- return S1x;
- else if (strncmp(model, "D1", 2) == 0)
- return D1x;
- else if (strncmp(model, "A1", 2) == 0)
- return A1x;
- else if (strncmp(model, "A2", 2) == 0)
- return A2x;
- else if (strncmp(model, "J1", 2) == 0)
- return S2x;
- else if (strncmp(model, "L5", 2) == 0)
- return L5x;
- else if (strncmp(model, "A4G", 3) == 0)
- return A4G;
- else if (strncmp(model, "W1N", 3) == 0)
- return W1N;
- else if (strncmp(model, "W3V", 3) == 0)
- return W3V;
- else if (strncmp(model, "W5A", 3) == 0)
- return W5A;
- else if (strncmp(model, "R1F", 3) == 0)
- return R1F;
- else if (strncmp(model, "A4S", 3) == 0)
- return A4S;
- else if (strncmp(model, "F3Sa", 4) == 0)
- return F3Sa;
- else
- return END_MODEL;
-}
-
-/*
- * This function is used to initialize the hotk with right values. In this
- * method, we can make all the detection we want, and modify the hotk struct
- */
-static int asus_hotk_get_info(void)
-{
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object *model = NULL;
- int bsts_result;
- char *string = NULL;
- acpi_status status;
-
- /*
- * Get DSDT headers early enough to allow for differentiating between
- * models, but late enough to allow acpi_bus_register_driver() to fail
- * before doing anything ACPI-specific. Should we encounter a machine,
- * which needs special handling (i.e. its hotkey device has a different
- * HID), this bit will be moved. A global variable asus_info contains
- * the DSDT header.
- */
- status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
- if (ACPI_FAILURE(status))
- pr_warn(" Couldn't get the DSDT table header\n");
-
- /* We have to write 0 on init this far for all ASUS models */
- if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
- pr_err(" Hotkey initialization failed\n");
- return -ENODEV;
- }
-
- /* This needs to be called for some laptops to init properly */
- if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result))
- pr_warn(" Error calling BSTS\n");
- else if (bsts_result)
- pr_notice(" BSTS called, 0x%02x returned\n", bsts_result);
-
- /*
- * Try to match the object returned by INIT to the specific model.
- * Handle every possible object (or the lack of thereof) the DSDT
- * writers might throw at us. When in trouble, we pass NULL to
- * asus_model_match() and try something completely different.
- */
- if (buffer.pointer) {
- model = buffer.pointer;
- switch (model->type) {
- case ACPI_TYPE_STRING:
- string = model->string.pointer;
- break;
- case ACPI_TYPE_BUFFER:
- string = model->buffer.pointer;
- break;
- default:
- kfree(model);
- model = NULL;
- break;
- }
- }
- hotk->model = asus_model_match(string);
- if (hotk->model == END_MODEL) { /* match failed */
- if (asus_info &&
- strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) {
- hotk->model = P30;
- pr_notice(" Samsung P30 detected, supported\n");
- hotk->methods = &model_conf[hotk->model];
- kfree(model);
- return 0;
- } else {
- hotk->model = M2E;
- pr_notice(" unsupported model %s, trying default values\n",
- string);
- pr_notice(" send /proc/acpi/dsdt to the developers\n");
- kfree(model);
- return -ENODEV;
- }
- }
- hotk->methods = &model_conf[hotk->model];
- pr_notice(" %s model detected, supported\n", string);
-
- /* Sort of per-model blacklist */
- if (strncmp(string, "L2B", 3) == 0)
- hotk->methods->lcd_status = NULL;
- /* L2B is similar enough to L3C to use its settings, with this only
- exception */
- else if (strncmp(string, "A3G", 3) == 0)
- hotk->methods->lcd_status = "\\BLFG";
- /* A3G is like M6R */
- else if (strncmp(string, "S5N", 3) == 0 ||
- strncmp(string, "M5N", 3) == 0 ||
- strncmp(string, "W3N", 3) == 0)
- hotk->methods->mt_mled = NULL;
- /* S5N, M5N and W3N have no MLED */
- else if (strncmp(string, "L5D", 3) == 0)
- hotk->methods->mt_wled = NULL;
- /* L5D's WLED is not controlled by ACPI */
- else if (strncmp(string, "M2N", 3) == 0 ||
- strncmp(string, "W3V", 3) == 0 ||
- strncmp(string, "S1N", 3) == 0)
- hotk->methods->mt_wled = "WLED";
- /* M2N, S1N and W3V have a usable WLED */
- else if (asus_info) {
- if (strncmp(asus_info->oem_table_id, "L1", 2) == 0)
- hotk->methods->mled_status = NULL;
- /* S1300A reports L84F, but L1400B too, account for that */
- }
-
- kfree(model);
-
- return 0;
-}
-
-static int asus_hotk_check(void)
-{
- int result = 0;
-
- result = acpi_bus_get_status(hotk->device);
- if (result)
- return result;
-
- if (hotk->device->status.present) {
- result = asus_hotk_get_info();
- } else {
- pr_err(" Hotkey device not present, aborting\n");
- return -EINVAL;
- }
-
- return result;
-}
-
-static int asus_hotk_found;
-
-static int asus_hotk_add(struct acpi_device *device)
-{
- acpi_status status = AE_OK;
- int result;
-
- pr_notice("Asus Laptop ACPI Extras version %s\n", ASUS_ACPI_VERSION);
-
- hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
- if (!hotk)
- return -ENOMEM;
-
- hotk->handle = device->handle;
- strcpy(acpi_device_name(device), ACPI_HOTK_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_HOTK_CLASS);
- device->driver_data = hotk;
- hotk->device = device;
-
- result = asus_hotk_check();
- if (result)
- goto end;
-
- result = asus_hotk_add_fs(device);
- if (result)
- goto end;
-
- /* For laptops without GPLV: init the hotk->brightness value */
- if ((!hotk->methods->brightness_get)
- && (!hotk->methods->brightness_status)
- && (hotk->methods->brightness_up && hotk->methods->brightness_down)) {
- status =
- acpi_evaluate_object(NULL, hotk->methods->brightness_down,
- NULL, NULL);
- if (ACPI_FAILURE(status))
- pr_warn(" Error changing brightness\n");
- else {
- status =
- acpi_evaluate_object(NULL,
- hotk->methods->brightness_up,
- NULL, NULL);
- if (ACPI_FAILURE(status))
- pr_warn(" Strange, error changing brightness\n");
- }
- }
-
- asus_hotk_found = 1;
-
- /* LED display is off by default */
- hotk->ledd_status = 0xFFF;
-
-end:
- if (result)
- kfree(hotk);
-
- return result;
-}
-
-static int asus_hotk_remove(struct acpi_device *device, int type)
-{
- asus_hotk_remove_fs(device);
-
- kfree(hotk);
-
- return 0;
-}
-
-static const struct backlight_ops asus_backlight_data = {
- .get_brightness = read_brightness,
- .update_status = set_brightness_status,
-};
-
-static void asus_acpi_exit(void)
-{
- if (asus_backlight_device)
- backlight_device_unregister(asus_backlight_device);
-
- acpi_bus_unregister_driver(&asus_hotk_driver);
- remove_proc_entry(PROC_ASUS, acpi_root_dir);
-
- return;
-}
-
-static int __init asus_acpi_init(void)
-{
- struct backlight_properties props;
- int result;
-
- result = acpi_bus_register_driver(&asus_hotk_driver);
- if (result < 0)
- return result;
-
- asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir);
- if (!asus_proc_dir) {
- pr_err("Unable to create /proc entry\n");
- acpi_bus_unregister_driver(&asus_hotk_driver);
- return -ENODEV;
- }
-
- /*
- * This is a bit of a kludge. We only want this module loaded
- * for ASUS systems, but there's currently no way to probe the
- * ACPI namespace for ASUS HIDs. So we just return failure if
- * we didn't find one, which will cause the module to be
- * unloaded.
- */
- if (!asus_hotk_found) {
- acpi_bus_unregister_driver(&asus_hotk_driver);
- remove_proc_entry(PROC_ASUS, acpi_root_dir);
- return -ENODEV;
- }
-
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_PLATFORM;
- props.max_brightness = 15;
- asus_backlight_device = backlight_device_register("asus", NULL, NULL,
- &asus_backlight_data,
- &props);
- if (IS_ERR(asus_backlight_device)) {
- pr_err("Could not register asus backlight device\n");
- asus_backlight_device = NULL;
- asus_acpi_exit();
- return -ENODEV;
- }
-
- return 0;
-}
-
-module_init(asus_acpi_init);
-module_exit(asus_acpi_exit);
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
index d967344..1887e2f 100644
--- a/drivers/platform/x86/compal-laptop.c
+++ b/drivers/platform/x86/compal-laptop.c
@@ -882,6 +882,7 @@ static struct dmi_system_id __initdata compal_dmi_table[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(dmi, compal_dmi_table);
static void initialize_power_supply_data(struct compal_data *data)
{
@@ -1097,16 +1098,3 @@ MODULE_AUTHOR("Roald Frederickx (roald.frederickx@gmail.com)");
MODULE_DESCRIPTION("Compal Laptop Support");
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-
-MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*");
-MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*");
-MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*");
-MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*");
-MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*");
-MODULE_ALIAS("dmi:*:rnJHL90:rvrREFERENCE:*");
-MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*");
-MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*");
-MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*");
-MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1012:*");
-MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1110:*");
-MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1210:*");
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index d93e962..a05fc9c 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -117,6 +117,7 @@ static const struct dmi_system_id __initdata dell_device_table[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(dmi, dell_device_table);
static struct dmi_system_id __devinitdata dell_blacklist[] = {
/* Supported by compal-laptop */
@@ -184,6 +185,33 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
},
.driver_data = &quirk_dell_vostro_v130,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Vostro 3555",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron N311z",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron M5110",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
};
static struct calling_interface_buffer *buffer;
@@ -236,9 +264,7 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
{
switch (dm->type) {
case 0xd4: /* Indexed IO */
- break;
case 0xd5: /* Protected Area Type 1 */
- break;
case 0xd6: /* Protected Area Type 2 */
break;
case 0xda: /* Calling interface */
@@ -615,6 +641,7 @@ static void touchpad_led_set(struct led_classdev *led_cdev,
static struct led_classdev touchpad_led = {
.name = "dell-laptop::touchpad",
.brightness_set = touchpad_led_set,
+ .flags = LED_CORE_SUSPENDRESUME,
};
static int __devinit touchpad_led_init(struct device *dev)
@@ -794,6 +821,3 @@ module_exit(dell_exit);
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_DESCRIPTION("Dell laptop driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*");
-MODULE_ALIAS("dmi:*svnDellInc.:*:ct9:*");
-MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*");
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index d9a9e2b..dab91b4 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -1251,6 +1251,14 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc)
/*
* ACPI driver
*/
+static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event)
+{
+ if (!eeepc->inputdev)
+ return ;
+ if (!sparse_keymap_report_event(eeepc->inputdev, event, 1, true))
+ pr_info("Unknown key %x pressed\n", event);
+}
+
static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
{
struct eeepc_laptop *eeepc = acpi_driver_data(device);
@@ -1287,12 +1295,11 @@ static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
* event will be desired value (or else ignored)
*/
}
- sparse_keymap_report_event(eeepc->inputdev, event,
- 1, true);
+ eeepc_input_notify(eeepc, event);
}
} else {
/* Everything else is a bona-fide keypress event */
- sparse_keymap_report_event(eeepc->inputdev, event, 1, true);
+ eeepc_input_notify(eeepc, event);
}
}
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c
index 9f6e643..6567613 100644
--- a/drivers/platform/x86/eeepc-wmi.c
+++ b/drivers/platform/x86/eeepc-wmi.c
@@ -32,6 +32,7 @@
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/dmi.h>
+#include <linux/fb.h>
#include <acpi/acpi_bus.h>
#include "asus-wmi.h"
@@ -84,9 +85,81 @@ static const struct key_entry eeepc_wmi_keymap[] = {
{ KE_KEY, 0xed, { KEY_CAMERA_DOWN } },
{ KE_KEY, 0xee, { KEY_CAMERA_LEFT } },
{ KE_KEY, 0xef, { KEY_CAMERA_RIGHT } },
+ { KE_KEY, 0xf3, { KEY_MENU } },
+ { KE_KEY, 0xf5, { KEY_HOMEPAGE } },
+ { KE_KEY, 0xf6, { KEY_ESC } },
{ KE_END, 0},
};
+static struct quirk_entry quirk_asus_unknown = {
+};
+
+static struct quirk_entry quirk_asus_1000h = {
+ .hotplug_wireless = true,
+};
+
+static struct quirk_entry quirk_asus_et2012_type1 = {
+ .store_backlight_power = true,
+};
+
+static struct quirk_entry quirk_asus_et2012_type3 = {
+ .scalar_panel_brightness = true,
+ .store_backlight_power = true,
+};
+
+static struct quirk_entry *quirks;
+
+static void et2012_quirks(void)
+{
+ const struct dmi_device *dev = NULL;
+ char oemstring[30];
+
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+ if (sscanf(dev->name, "AEMS%24c", oemstring) == 1) {
+ if (oemstring[18] == '1')
+ quirks = &quirk_asus_et2012_type1;
+ else if (oemstring[18] == '3')
+ quirks = &quirk_asus_et2012_type3;
+ break;
+ }
+ }
+}
+
+static int dmi_matched(const struct dmi_system_id *dmi)
+{
+ char *model;
+
+ quirks = dmi->driver_data;
+
+ model = (char *)dmi->matches[1].substr;
+ if (unlikely(strncmp(model, "ET2012", 6) == 0))
+ et2012_quirks();
+
+ return 1;
+}
+
+static struct dmi_system_id asus_quirks[] = {
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK Computer INC. 1000H",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "1000H"),
+ },
+ .driver_data = &quirk_asus_1000h,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK Computer INC. ET2012E/I",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ET2012"),
+ },
+ .driver_data = &quirk_asus_unknown,
+ },
+ {},
+};
+
static void eeepc_wmi_key_filter(struct asus_wmi_driver *asus_wmi, int *code,
unsigned int *value, bool *autorelease)
{
@@ -141,33 +214,16 @@ static int eeepc_wmi_probe(struct platform_device *pdev)
return 0;
}
-static void eeepc_dmi_check(struct asus_wmi_driver *driver)
-{
- const char *model;
-
- model = dmi_get_system_info(DMI_PRODUCT_NAME);
- if (!model)
- return;
-
- /*
- * Whitelist for wlan hotplug
- *
- * Asus 1000H needs the current hotplug code to handle
- * Fn+F2 correctly. We may add other Asus here later, but
- * it seems that most of the laptops supported by asus-wmi
- * don't need to be on this list
- */
- if (strcmp(model, "1000H") == 0) {
- driver->hotplug_wireless = true;
- pr_info("wlan hotplug enabled\n");
- }
-}
-
static void eeepc_wmi_quirks(struct asus_wmi_driver *driver)
{
- driver->hotplug_wireless = hotplug_wireless;
- driver->wapf = -1;
- eeepc_dmi_check(driver);
+ quirks = &quirk_asus_unknown;
+ quirks->hotplug_wireless = hotplug_wireless;
+
+ dmi_check_system(asus_quirks);
+
+ driver->quirks = quirks;
+ driver->quirks->wapf = -1;
+ driver->panel_power = FB_BLANK_UNBLANK;
}
static struct asus_wmi_driver asus_wmi_driver = {
@@ -179,7 +235,7 @@ static struct asus_wmi_driver asus_wmi_driver = {
.input_phys = EEEPC_WMI_FILE "/input0",
.key_filter = eeepc_wmi_key_filter,
.probe = eeepc_wmi_probe,
- .quirks = eeepc_wmi_quirks,
+ .detect_quirks = eeepc_wmi_quirks,
};
diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c
index ba68d4e..7387f97 100644
--- a/drivers/platform/x86/hdaps.c
+++ b/drivers/platform/x86/hdaps.c
@@ -375,7 +375,7 @@ static ssize_t hdaps_variance_show(struct device *dev,
static ssize_t hdaps_temp1_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- u8 temp;
+ u8 uninitialized_var(temp);
int ret;
ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp);
@@ -388,7 +388,7 @@ static ssize_t hdaps_temp1_show(struct device *dev,
static ssize_t hdaps_temp2_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- u8 temp;
+ u8 uninitialized_var(temp);
int ret;
ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp);
diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c
index 88a98cf..f7ba316e 100644
--- a/drivers/platform/x86/intel_ips.c
+++ b/drivers/platform/x86/intel_ips.c
@@ -609,25 +609,16 @@ static bool mcp_exceeded(struct ips_driver *ips)
bool ret = false;
u32 temp_limit;
u32 avg_power;
- const char *msg = "MCP limit exceeded: ";
spin_lock_irqsave(&ips->turbo_status_lock, flags);
temp_limit = ips->mcp_temp_limit * 100;
- if (ips->mcp_avg_temp > temp_limit) {
- dev_info(&ips->dev->dev,
- "%sAvg temp %u, limit %u\n", msg, ips->mcp_avg_temp,
- temp_limit);
+ if (ips->mcp_avg_temp > temp_limit)
ret = true;
- }
avg_power = ips->cpu_avg_power + ips->mch_avg_power;
- if (avg_power > ips->mcp_power_limit) {
- dev_info(&ips->dev->dev,
- "%sAvg power %u, limit %u\n", msg, avg_power,
- ips->mcp_power_limit);
+ if (avg_power > ips->mcp_power_limit)
ret = true;
- }
spin_unlock_irqrestore(&ips->turbo_status_lock, flags);
diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c
index 0903a88..0a3594c 100644
--- a/drivers/platform/x86/intel_mid_powerbtn.c
+++ b/drivers/platform/x86/intel_mid_powerbtn.c
@@ -142,17 +142,7 @@ static struct platform_driver mfld_pb_driver = {
.remove = __devexit_p(mfld_pb_remove),
};
-static int __init mfld_pb_init(void)
-{
- return platform_driver_register(&mfld_pb_driver);
-}
-module_init(mfld_pb_init);
-
-static void __exit mfld_pb_exit(void)
-{
- platform_driver_unregister(&mfld_pb_driver);
-}
-module_exit(mfld_pb_exit);
+module_platform_driver(mfld_pb_driver);
MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>");
MODULE_DESCRIPTION("Intel Medfield Power Button Driver");
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c
index 2ee9766..5ae9cd9 100644
--- a/drivers/platform/x86/intel_mid_thermal.c
+++ b/drivers/platform/x86/intel_mid_thermal.c
@@ -549,6 +549,7 @@ static int mid_thermal_remove(struct platform_device *pdev)
static const struct platform_device_id therm_id_table[] = {
{ DRIVER_NAME, 1 },
+ { "msic_thermal", 1 },
{ }
};
@@ -564,18 +565,7 @@ static struct platform_driver mid_thermal_driver = {
.id_table = therm_id_table,
};
-static int __init mid_thermal_module_init(void)
-{
- return platform_driver_register(&mid_thermal_driver);
-}
-
-static void __exit mid_thermal_module_exit(void)
-{
- platform_driver_unregister(&mid_thermal_driver);
-}
-
-module_init(mid_thermal_module_init);
-module_exit(mid_thermal_module_exit);
+module_platform_driver(mid_thermal_driver);
MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>");
MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver");
diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c
index 6ee0b5c..79a0c2f 100644
--- a/drivers/platform/x86/intel_oaktrail.c
+++ b/drivers/platform/x86/intel_oaktrail.c
@@ -313,6 +313,7 @@ static struct dmi_system_id __initdata oaktrail_dmi_table[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(dmi, oaktrail_dmi_table);
static int __init oaktrail_init(void)
{
@@ -394,4 +395,3 @@ MODULE_AUTHOR("Yin Kangkai (kangkai.yin@intel.com)");
MODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras");
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("dmi:*:svnIntelCorporation:pnOakTrailplatform:*");
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index fd73ea8..e2a34b4 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -17,10 +17,18 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/backlight.h>
+#include <linux/leds.h>
#include <linux/fb.h>
#include <linux/dmi.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
+#include <linux/acpi.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/ctype.h>
+#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
+#include <acpi/video.h>
+#endif
/*
* This driver is needed because a number of Samsung laptops do not hook
@@ -41,9 +49,20 @@
#define SABI_IFACE_COMPLETE 0x04
#define SABI_IFACE_DATA 0x05
-/* Structure to get data back to the calling function */
-struct sabi_retval {
- u8 retval[20];
+#define WL_STATUS_WLAN 0x0
+#define WL_STATUS_BT 0x2
+
+/* Structure get/set data using sabi */
+struct sabi_data {
+ union {
+ struct {
+ u32 d0;
+ u32 d1;
+ u16 d2;
+ u8 d3;
+ };
+ u8 data[11];
+ };
};
struct sabi_header_offsets {
@@ -60,8 +79,8 @@ struct sabi_commands {
* Brightness is 0 - 8, as described above.
* Value 0 is for the BIOS to use
*/
- u8 get_brightness;
- u8 set_brightness;
+ u16 get_brightness;
+ u16 set_brightness;
/*
* first byte:
@@ -72,40 +91,56 @@ struct sabi_commands {
* 0x03 - 3G is on
* TODO, verify 3G is correct, that doesn't seem right...
*/
- u8 get_wireless_button;
- u8 set_wireless_button;
+ u16 get_wireless_button;
+ u16 set_wireless_button;
/* 0 is off, 1 is on */
- u8 get_backlight;
- u8 set_backlight;
+ u16 get_backlight;
+ u16 set_backlight;
/*
* 0x80 or 0x00 - no action
* 0x81 - recovery key pressed
*/
- u8 get_recovery_mode;
- u8 set_recovery_mode;
+ u16 get_recovery_mode;
+ u16 set_recovery_mode;
/*
* on seclinux: 0 is low, 1 is high,
* on swsmi: 0 is normal, 1 is silent, 2 is turbo
*/
- u8 get_performance_level;
- u8 set_performance_level;
+ u16 get_performance_level;
+ u16 set_performance_level;
+
+ /* 0x80 is off, 0x81 is on */
+ u16 get_battery_life_extender;
+ u16 set_battery_life_extender;
+
+ /* 0x80 is off, 0x81 is on */
+ u16 get_usb_charge;
+ u16 set_usb_charge;
+
+ /* the first byte is for bluetooth and the third one is for wlan */
+ u16 get_wireless_status;
+ u16 set_wireless_status;
+
+ /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */
+ u16 kbd_backlight;
/*
* Tell the BIOS that Linux is running on this machine.
* 81 is on, 80 is off
*/
- u8 set_linux;
+ u16 set_linux;
};
struct sabi_performance_level {
const char *name;
- u8 value;
+ u16 value;
};
struct sabi_config {
+ int sabi_version;
const char *test_string;
u16 main_function;
const struct sabi_header_offsets header_offsets;
@@ -117,6 +152,10 @@ struct sabi_config {
static const struct sabi_config sabi_configs[] = {
{
+ /* I don't know if it is really 2, but it it is
+ * less than 3 anyway */
+ .sabi_version = 2,
+
.test_string = "SECLINUX",
.main_function = 0x4c49,
@@ -146,6 +185,17 @@ static const struct sabi_config sabi_configs[] = {
.get_performance_level = 0x08,
.set_performance_level = 0x09,
+ .get_battery_life_extender = 0xFFFF,
+ .set_battery_life_extender = 0xFFFF,
+
+ .get_usb_charge = 0xFFFF,
+ .set_usb_charge = 0xFFFF,
+
+ .get_wireless_status = 0xFFFF,
+ .set_wireless_status = 0xFFFF,
+
+ .kbd_backlight = 0xFFFF,
+
.set_linux = 0x0a,
},
@@ -164,6 +214,8 @@ static const struct sabi_config sabi_configs[] = {
.max_brightness = 8,
},
{
+ .sabi_version = 3,
+
.test_string = "SwSmi@",
.main_function = 0x5843,
@@ -193,6 +245,17 @@ static const struct sabi_config sabi_configs[] = {
.get_performance_level = 0x31,
.set_performance_level = 0x32,
+ .get_battery_life_extender = 0x65,
+ .set_battery_life_extender = 0x66,
+
+ .get_usb_charge = 0x67,
+ .set_usb_charge = 0x68,
+
+ .get_wireless_status = 0x69,
+ .set_wireless_status = 0x6a,
+
+ .kbd_backlight = 0x78,
+
.set_linux = 0xff,
},
@@ -217,16 +280,82 @@ static const struct sabi_config sabi_configs[] = {
{ },
};
-static const struct sabi_config *sabi_config;
+/*
+ * samsung-laptop/ - debugfs root directory
+ * f0000_segment - dump f0000 segment
+ * command - current command
+ * data - current data
+ * d0, d1, d2, d3 - data fields
+ * call - call SABI using command and data
+ *
+ * This allow to call arbitrary sabi commands wihout
+ * modifying the driver at all.
+ * For example, setting the keyboard backlight brightness to 5
+ *
+ * echo 0x78 > command
+ * echo 0x0582 > d0
+ * echo 0 > d1
+ * echo 0 > d2
+ * echo 0 > d3
+ * cat call
+ */
+
+struct samsung_laptop_debug {
+ struct dentry *root;
+ struct sabi_data data;
+ u16 command;
+
+ struct debugfs_blob_wrapper f0000_wrapper;
+ struct debugfs_blob_wrapper data_wrapper;
+ struct debugfs_blob_wrapper sdiag_wrapper;
+};
+
+struct samsung_laptop;
+
+struct samsung_rfkill {
+ struct samsung_laptop *samsung;
+ struct rfkill *rfkill;
+ enum rfkill_type type;
+};
+
+struct samsung_laptop {
+ const struct sabi_config *config;
+
+ void __iomem *sabi;
+ void __iomem *sabi_iface;
+ void __iomem *f0000_segment;
+
+ struct mutex sabi_mutex;
+
+ struct platform_device *platform_device;
+ struct backlight_device *backlight_device;
+
+ struct samsung_rfkill wlan;
+ struct samsung_rfkill bluetooth;
+
+ struct led_classdev kbd_led;
+ int kbd_led_wk;
+ struct workqueue_struct *led_workqueue;
+ struct work_struct kbd_led_work;
+
+ struct samsung_laptop_debug debug;
+ struct samsung_quirks *quirks;
+
+ bool handle_backlight;
+ bool has_stepping_quirk;
+
+ char sdiag[64];
+};
+
+struct samsung_quirks {
+ bool broken_acpi_video;
+};
+
+static struct samsung_quirks samsung_unknown = {};
-static void __iomem *sabi;
-static void __iomem *sabi_iface;
-static void __iomem *f0000_segment;
-static struct backlight_device *backlight_device;
-static struct mutex sabi_mutex;
-static struct platform_device *sdev;
-static struct rfkill *rfk;
-static bool has_stepping_quirk;
+static struct samsung_quirks samsung_broken_acpi_video = {
+ .broken_acpi_video = true,
+};
static bool force;
module_param(force, bool, 0);
@@ -237,176 +366,143 @@ static bool debug;
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");
-static int sabi_get_command(u8 command, struct sabi_retval *sretval)
+static int sabi_command(struct samsung_laptop *samsung, u16 command,
+ struct sabi_data *in,
+ struct sabi_data *out)
{
- int retval = 0;
- u16 port = readw(sabi + sabi_config->header_offsets.port);
+ const struct sabi_config *config = samsung->config;
+ int ret = 0;
+ u16 port = readw(samsung->sabi + config->header_offsets.port);
u8 complete, iface_data;
- mutex_lock(&sabi_mutex);
-
- /* enable memory to be able to write to it */
- outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
-
- /* write out the command */
- writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
- writew(command, sabi_iface + SABI_IFACE_SUB);
- writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
- outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
-
- /* write protect memory to make it safe */
- outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
+ mutex_lock(&samsung->sabi_mutex);
- /* see if the command actually succeeded */
- complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
- iface_data = readb(sabi_iface + SABI_IFACE_DATA);
- if (complete != 0xaa || iface_data == 0xff) {
- pr_warn("SABI get command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
- command, complete, iface_data);
- retval = -EINVAL;
- goto exit;
+ if (debug) {
+ if (in)
+ pr_info("SABI command:0x%04x "
+ "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}",
+ command, in->d0, in->d1, in->d2, in->d3);
+ else
+ pr_info("SABI command:0x%04x", command);
}
- /*
- * Save off the data into a structure so the caller use it.
- * Right now we only want the first 4 bytes,
- * There are commands that need more, but not for the ones we
- * currently care about.
- */
- sretval->retval[0] = readb(sabi_iface + SABI_IFACE_DATA);
- sretval->retval[1] = readb(sabi_iface + SABI_IFACE_DATA + 1);
- sretval->retval[2] = readb(sabi_iface + SABI_IFACE_DATA + 2);
- sretval->retval[3] = readb(sabi_iface + SABI_IFACE_DATA + 3);
-
-exit:
- mutex_unlock(&sabi_mutex);
- return retval;
-
-}
-
-static int sabi_set_command(u8 command, u8 data)
-{
- int retval = 0;
- u16 port = readw(sabi + sabi_config->header_offsets.port);
- u8 complete, iface_data;
-
- mutex_lock(&sabi_mutex);
/* enable memory to be able to write to it */
- outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
+ outb(readb(samsung->sabi + config->header_offsets.en_mem), port);
/* write out the command */
- writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
- writew(command, sabi_iface + SABI_IFACE_SUB);
- writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
- writeb(data, sabi_iface + SABI_IFACE_DATA);
- outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
+ writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN);
+ writew(command, samsung->sabi_iface + SABI_IFACE_SUB);
+ writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE);
+ if (in) {
+ writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA);
+ writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4);
+ writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8);
+ writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10);
+ }
+ outb(readb(samsung->sabi + config->header_offsets.iface_func), port);
/* write protect memory to make it safe */
- outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
+ outb(readb(samsung->sabi + config->header_offsets.re_mem), port);
/* see if the command actually succeeded */
- complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
- iface_data = readb(sabi_iface + SABI_IFACE_DATA);
+ complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE);
+ iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA);
+
+ /* iface_data = 0xFF happens when a command is not known
+ * so we only add a warning in debug mode since we will
+ * probably issue some unknown command at startup to find
+ * out which features are supported */
+ if (complete != 0xaa || (iface_data == 0xff && debug))
+ pr_warn("SABI command 0x%04x failed with"
+ " completion flag 0x%02x and interface data 0x%02x",
+ command, complete, iface_data);
+
if (complete != 0xaa || iface_data == 0xff) {
- pr_warn("SABI set command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
- command, complete, iface_data);
- retval = -EINVAL;
+ ret = -EINVAL;
+ goto exit;
}
- mutex_unlock(&sabi_mutex);
- return retval;
-}
-
-static void test_backlight(void)
-{
- struct sabi_retval sretval;
-
- sabi_get_command(sabi_config->commands.get_backlight, &sretval);
- printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
-
- sabi_set_command(sabi_config->commands.set_backlight, 0);
- printk(KERN_DEBUG "backlight should be off\n");
-
- sabi_get_command(sabi_config->commands.get_backlight, &sretval);
- printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
-
- msleep(1000);
+ if (out) {
+ out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA);
+ out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4);
+ out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2);
+ out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1);
+ }
- sabi_set_command(sabi_config->commands.set_backlight, 1);
- printk(KERN_DEBUG "backlight should be on\n");
+ if (debug && out) {
+ pr_info("SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}",
+ out->d0, out->d1, out->d2, out->d3);
+ }
- sabi_get_command(sabi_config->commands.get_backlight, &sretval);
- printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
+exit:
+ mutex_unlock(&samsung->sabi_mutex);
+ return ret;
}
-static void test_wireless(void)
+/* simple wrappers usable with most commands */
+static int sabi_set_commandb(struct samsung_laptop *samsung,
+ u16 command, u8 data)
{
- struct sabi_retval sretval;
-
- sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
- printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
-
- sabi_set_command(sabi_config->commands.set_wireless_button, 0);
- printk(KERN_DEBUG "wireless led should be off\n");
-
- sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
- printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
+ struct sabi_data in = { { { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } };
- msleep(1000);
-
- sabi_set_command(sabi_config->commands.set_wireless_button, 1);
- printk(KERN_DEBUG "wireless led should be on\n");
-
- sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
- printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
+ in.data[0] = data;
+ return sabi_command(samsung, command, &in, NULL);
}
-static u8 read_brightness(void)
+static int read_brightness(struct samsung_laptop *samsung)
{
- struct sabi_retval sretval;
+ const struct sabi_config *config = samsung->config;
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data sretval;
int user_brightness = 0;
int retval;
- retval = sabi_get_command(sabi_config->commands.get_brightness,
- &sretval);
- if (!retval) {
- user_brightness = sretval.retval[0];
- if (user_brightness > sabi_config->min_brightness)
- user_brightness -= sabi_config->min_brightness;
- else
- user_brightness = 0;
- }
+ retval = sabi_command(samsung, commands->get_brightness,
+ NULL, &sretval);
+ if (retval)
+ return retval;
+
+ user_brightness = sretval.data[0];
+ if (user_brightness > config->min_brightness)
+ user_brightness -= config->min_brightness;
+ else
+ user_brightness = 0;
+
return user_brightness;
}
-static void set_brightness(u8 user_brightness)
+static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness)
{
- u8 user_level = user_brightness + sabi_config->min_brightness;
+ const struct sabi_config *config = samsung->config;
+ const struct sabi_commands *commands = &samsung->config->commands;
+ u8 user_level = user_brightness + config->min_brightness;
- if (has_stepping_quirk && user_level != 0) {
+ if (samsung->has_stepping_quirk && user_level != 0) {
/*
* short circuit if the specified level is what's already set
* to prevent the screen from flickering needlessly
*/
- if (user_brightness == read_brightness())
+ if (user_brightness == read_brightness(samsung))
return;
- sabi_set_command(sabi_config->commands.set_brightness, 0);
+ sabi_set_commandb(samsung, commands->set_brightness, 0);
}
- sabi_set_command(sabi_config->commands.set_brightness, user_level);
+ sabi_set_commandb(samsung, commands->set_brightness, user_level);
}
static int get_brightness(struct backlight_device *bd)
{
- return (int)read_brightness();
+ struct samsung_laptop *samsung = bl_get_data(bd);
+
+ return read_brightness(samsung);
}
-static void check_for_stepping_quirk(void)
+static void check_for_stepping_quirk(struct samsung_laptop *samsung)
{
- u8 initial_level;
- u8 check_level;
- u8 orig_level = read_brightness();
+ int initial_level;
+ int check_level;
+ int orig_level = read_brightness(samsung);
/*
* Some laptops exhibit the strange behaviour of stepping toward
@@ -416,34 +512,38 @@ static void check_for_stepping_quirk(void)
*/
if (orig_level == 0)
- set_brightness(1);
+ set_brightness(samsung, 1);
- initial_level = read_brightness();
+ initial_level = read_brightness(samsung);
if (initial_level <= 2)
check_level = initial_level + 2;
else
check_level = initial_level - 2;
- has_stepping_quirk = false;
- set_brightness(check_level);
+ samsung->has_stepping_quirk = false;
+ set_brightness(samsung, check_level);
- if (read_brightness() != check_level) {
- has_stepping_quirk = true;
+ if (read_brightness(samsung) != check_level) {
+ samsung->has_stepping_quirk = true;
pr_info("enabled workaround for brightness stepping quirk\n");
}
- set_brightness(orig_level);
+ set_brightness(samsung, orig_level);
}
static int update_status(struct backlight_device *bd)
{
- set_brightness(bd->props.brightness);
+ struct samsung_laptop *samsung = bl_get_data(bd);
+ const struct sabi_commands *commands = &samsung->config->commands;
+
+ set_brightness(samsung, bd->props.brightness);
if (bd->props.power == FB_BLANK_UNBLANK)
- sabi_set_command(sabi_config->commands.set_backlight, 1);
+ sabi_set_commandb(samsung, commands->set_backlight, 1);
else
- sabi_set_command(sabi_config->commands.set_backlight, 0);
+ sabi_set_commandb(samsung, commands->set_backlight, 0);
+
return 0;
}
@@ -452,66 +552,101 @@ static const struct backlight_ops backlight_ops = {
.update_status = update_status,
};
-static int rfkill_set(void *data, bool blocked)
+static int seclinux_rfkill_set(void *data, bool blocked)
{
- /* Do something with blocked...*/
- /*
- * blocked == false is on
- * blocked == true is off
- */
- if (blocked)
- sabi_set_command(sabi_config->commands.set_wireless_button, 0);
- else
- sabi_set_command(sabi_config->commands.set_wireless_button, 1);
+ struct samsung_rfkill *srfkill = data;
+ struct samsung_laptop *samsung = srfkill->samsung;
+ const struct sabi_commands *commands = &samsung->config->commands;
- return 0;
+ return sabi_set_commandb(samsung, commands->set_wireless_button,
+ !blocked);
}
-static struct rfkill_ops rfkill_ops = {
- .set_block = rfkill_set,
+static struct rfkill_ops seclinux_rfkill_ops = {
+ .set_block = seclinux_rfkill_set,
};
-static int init_wireless(struct platform_device *sdev)
+static int swsmi_wireless_status(struct samsung_laptop *samsung,
+ struct sabi_data *data)
{
- int retval;
+ const struct sabi_commands *commands = &samsung->config->commands;
- rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN,
- &rfkill_ops, NULL);
- if (!rfk)
- return -ENOMEM;
-
- retval = rfkill_register(rfk);
- if (retval) {
- rfkill_destroy(rfk);
- return -ENODEV;
- }
+ return sabi_command(samsung, commands->get_wireless_status,
+ NULL, data);
+}
- return 0;
+static int swsmi_rfkill_set(void *priv, bool blocked)
+{
+ struct samsung_rfkill *srfkill = priv;
+ struct samsung_laptop *samsung = srfkill->samsung;
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+ int ret, i;
+
+ ret = swsmi_wireless_status(samsung, &data);
+ if (ret)
+ return ret;
+
+ /* Don't set the state for non-present devices */
+ for (i = 0; i < 4; i++)
+ if (data.data[i] == 0x02)
+ data.data[1] = 0;
+
+ if (srfkill->type == RFKILL_TYPE_WLAN)
+ data.data[WL_STATUS_WLAN] = !blocked;
+ else if (srfkill->type == RFKILL_TYPE_BLUETOOTH)
+ data.data[WL_STATUS_BT] = !blocked;
+
+ return sabi_command(samsung, commands->set_wireless_status,
+ &data, &data);
}
-static void destroy_wireless(void)
+static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv)
{
- rfkill_unregister(rfk);
- rfkill_destroy(rfk);
+ struct samsung_rfkill *srfkill = priv;
+ struct samsung_laptop *samsung = srfkill->samsung;
+ struct sabi_data data;
+ int ret;
+
+ ret = swsmi_wireless_status(samsung, &data);
+ if (ret)
+ return ;
+
+ if (srfkill->type == RFKILL_TYPE_WLAN)
+ ret = data.data[WL_STATUS_WLAN];
+ else if (srfkill->type == RFKILL_TYPE_BLUETOOTH)
+ ret = data.data[WL_STATUS_BT];
+ else
+ return ;
+
+ rfkill_set_sw_state(rfkill, !ret);
}
+static struct rfkill_ops swsmi_rfkill_ops = {
+ .set_block = swsmi_rfkill_set,
+ .query = swsmi_rfkill_query,
+};
+
static ssize_t get_performance_level(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct sabi_retval sretval;
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
+ const struct sabi_config *config = samsung->config;
+ const struct sabi_commands *commands = &config->commands;
+ struct sabi_data sretval;
int retval;
int i;
/* Read the state */
- retval = sabi_get_command(sabi_config->commands.get_performance_level,
- &sretval);
+ retval = sabi_command(samsung, commands->get_performance_level,
+ NULL, &sretval);
if (retval)
return retval;
/* The logic is backwards, yeah, lots of fun... */
- for (i = 0; sabi_config->performance_levels[i].name; ++i) {
- if (sretval.retval[0] == sabi_config->performance_levels[i].value)
- return sprintf(buf, "%s\n", sabi_config->performance_levels[i].name);
+ for (i = 0; config->performance_levels[i].name; ++i) {
+ if (sretval.data[0] == config->performance_levels[i].value)
+ return sprintf(buf, "%s\n", config->performance_levels[i].name);
}
return sprintf(buf, "%s\n", "unknown");
}
@@ -520,269 +655,178 @@ static ssize_t set_performance_level(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
- if (count >= 1) {
- int i;
- for (i = 0; sabi_config->performance_levels[i].name; ++i) {
- const struct sabi_performance_level *level =
- &sabi_config->performance_levels[i];
- if (!strncasecmp(level->name, buf, strlen(level->name))) {
- sabi_set_command(sabi_config->commands.set_performance_level,
- level->value);
- break;
- }
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
+ const struct sabi_config *config = samsung->config;
+ const struct sabi_commands *commands = &config->commands;
+ int i;
+
+ if (count < 1)
+ return count;
+
+ for (i = 0; config->performance_levels[i].name; ++i) {
+ const struct sabi_performance_level *level =
+ &config->performance_levels[i];
+ if (!strncasecmp(level->name, buf, strlen(level->name))) {
+ sabi_set_commandb(samsung,
+ commands->set_performance_level,
+ level->value);
+ break;
}
- if (!sabi_config->performance_levels[i].name)
- return -EINVAL;
}
+
+ if (!config->performance_levels[i].name)
+ return -EINVAL;
+
return count;
}
+
static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
get_performance_level, set_performance_level);
+static int read_battery_life_extender(struct samsung_laptop *samsung)
+{
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+ int retval;
+
+ if (commands->get_battery_life_extender == 0xFFFF)
+ return -ENODEV;
+
+ memset(&data, 0, sizeof(data));
+ data.data[0] = 0x80;
+ retval = sabi_command(samsung, commands->get_battery_life_extender,
+ &data, &data);
-static int __init dmi_check_cb(const struct dmi_system_id *id)
+ if (retval)
+ return retval;
+
+ if (data.data[0] != 0 && data.data[0] != 1)
+ return -ENODEV;
+
+ return data.data[0];
+}
+
+static int write_battery_life_extender(struct samsung_laptop *samsung,
+ int enabled)
{
- pr_info("found laptop model '%s'\n",
- id->ident);
- return 1;
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+
+ memset(&data, 0, sizeof(data));
+ data.data[0] = 0x80 | enabled;
+ return sabi_command(samsung, commands->set_battery_life_extender,
+ &data, NULL);
}
-static struct dmi_system_id __initdata samsung_dmi_table[] = {
- {
- .ident = "N128",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
- DMI_MATCH(DMI_BOARD_NAME, "N128"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N130",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
- DMI_MATCH(DMI_BOARD_NAME, "N130"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N510",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N510"),
- DMI_MATCH(DMI_BOARD_NAME, "N510"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X125",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X125"),
- DMI_MATCH(DMI_BOARD_NAME, "X125"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X120/X170",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"),
- DMI_MATCH(DMI_BOARD_NAME, "X120/X170"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NC10",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
- DMI_MATCH(DMI_BOARD_NAME, "NC10"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NP-Q45",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
- DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X360",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
- DMI_MATCH(DMI_BOARD_NAME, "X360"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R410 Plus",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R410P"),
- DMI_MATCH(DMI_BOARD_NAME, "R460"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R518",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R518"),
- DMI_MATCH(DMI_BOARD_NAME, "R518"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R519/R719",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"),
- DMI_MATCH(DMI_BOARD_NAME, "R519/R719"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N150/N210/N220",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
- DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N220",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N220"),
- DMI_MATCH(DMI_BOARD_NAME, "N220"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N150/N210/N220/N230",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"),
- DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N150P/N210P/N220P",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"),
- DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R700",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "SR700"),
- DMI_MATCH(DMI_BOARD_NAME, "SR700"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R530/R730",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"),
- DMI_MATCH(DMI_BOARD_NAME, "R530/R730"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NF110/NF210/NF310",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
- DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N145P/N250P/N260P",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
- DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R70/R71",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"),
- DMI_MATCH(DMI_BOARD_NAME, "R70/R71"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "P460",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "P460"),
- DMI_MATCH(DMI_BOARD_NAME, "P460"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R528/R728",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R528/R728"),
- DMI_MATCH(DMI_BOARD_NAME, "R528/R728"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NC210/NC110",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"),
- DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X520",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X520"),
- DMI_MATCH(DMI_BOARD_NAME, "X520"),
- },
- .callback = dmi_check_cb,
- },
- { },
+static ssize_t get_battery_life_extender(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
+ int ret;
+
+ ret = read_battery_life_extender(samsung);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t set_battery_life_extender(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
+ int ret, value;
+
+ if (!count || sscanf(buf, "%i", &value) != 1)
+ return -EINVAL;
+
+ ret = write_battery_life_extender(samsung, !!value);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO,
+ get_battery_life_extender, set_battery_life_extender);
+
+static int read_usb_charge(struct samsung_laptop *samsung)
+{
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+ int retval;
+
+ if (commands->get_usb_charge == 0xFFFF)
+ return -ENODEV;
+
+ memset(&data, 0, sizeof(data));
+ data.data[0] = 0x80;
+ retval = sabi_command(samsung, commands->get_usb_charge,
+ &data, &data);
+
+ if (retval)
+ return retval;
+
+ if (data.data[0] != 0 && data.data[0] != 1)
+ return -ENODEV;
+
+ return data.data[0];
+}
+
+static int write_usb_charge(struct samsung_laptop *samsung,
+ int enabled)
+{
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+
+ memset(&data, 0, sizeof(data));
+ data.data[0] = 0x80 | enabled;
+ return sabi_command(samsung, commands->set_usb_charge,
+ &data, NULL);
+}
+
+static ssize_t get_usb_charge(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
+ int ret;
+
+ ret = read_usb_charge(samsung);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t set_usb_charge(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
+ int ret, value;
+
+ if (!count || sscanf(buf, "%i", &value) != 1)
+ return -EINVAL;
+
+ ret = write_usb_charge(samsung, !!value);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO,
+ get_usb_charge, set_usb_charge);
+
+static struct attribute *platform_attributes[] = {
+ &dev_attr_performance_level.attr,
+ &dev_attr_battery_life_extender.attr,
+ &dev_attr_usb_charge.attr,
+ NULL
};
-MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
static int find_signature(void __iomem *memcheck, const char *testStr)
{
@@ -803,153 +847,772 @@ static int find_signature(void __iomem *memcheck, const char *testStr)
return loca;
}
-static int __init samsung_init(void)
+static void samsung_rfkill_exit(struct samsung_laptop *samsung)
{
- struct backlight_properties props;
- struct sabi_retval sretval;
- unsigned int ifaceP;
- int i;
- int loca;
+ if (samsung->wlan.rfkill) {
+ rfkill_unregister(samsung->wlan.rfkill);
+ rfkill_destroy(samsung->wlan.rfkill);
+ samsung->wlan.rfkill = NULL;
+ }
+ if (samsung->bluetooth.rfkill) {
+ rfkill_unregister(samsung->bluetooth.rfkill);
+ rfkill_destroy(samsung->bluetooth.rfkill);
+ samsung->bluetooth.rfkill = NULL;
+ }
+}
+
+static int samsung_new_rfkill(struct samsung_laptop *samsung,
+ struct samsung_rfkill *arfkill,
+ const char *name, enum rfkill_type type,
+ const struct rfkill_ops *ops,
+ int blocked)
+{
+ struct rfkill **rfkill = &arfkill->rfkill;
+ int ret;
+
+ arfkill->type = type;
+ arfkill->samsung = samsung;
+
+ *rfkill = rfkill_alloc(name, &samsung->platform_device->dev,
+ type, ops, arfkill);
+
+ if (!*rfkill)
+ return -EINVAL;
+
+ if (blocked != -1)
+ rfkill_init_sw_state(*rfkill, blocked);
+
+ ret = rfkill_register(*rfkill);
+ if (ret) {
+ rfkill_destroy(*rfkill);
+ *rfkill = NULL;
+ return ret;
+ }
+ return 0;
+}
+
+static int __init samsung_rfkill_init_seclinux(struct samsung_laptop *samsung)
+{
+ return samsung_new_rfkill(samsung, &samsung->wlan, "samsung-wlan",
+ RFKILL_TYPE_WLAN, &seclinux_rfkill_ops, -1);
+}
+
+static int __init samsung_rfkill_init_swsmi(struct samsung_laptop *samsung)
+{
+ struct sabi_data data;
+ int ret;
+
+ ret = swsmi_wireless_status(samsung, &data);
+ if (ret) {
+ /* Some swsmi laptops use the old seclinux way to control
+ * wireless devices */
+ if (ret == -EINVAL)
+ ret = samsung_rfkill_init_seclinux(samsung);
+ return ret;
+ }
+
+ /* 0x02 seems to mean that the device is no present/available */
+
+ if (data.data[WL_STATUS_WLAN] != 0x02)
+ ret = samsung_new_rfkill(samsung, &samsung->wlan,
+ "samsung-wlan",
+ RFKILL_TYPE_WLAN,
+ &swsmi_rfkill_ops,
+ !data.data[WL_STATUS_WLAN]);
+ if (ret)
+ goto exit;
+
+ if (data.data[WL_STATUS_BT] != 0x02)
+ ret = samsung_new_rfkill(samsung, &samsung->bluetooth,
+ "samsung-bluetooth",
+ RFKILL_TYPE_BLUETOOTH,
+ &swsmi_rfkill_ops,
+ !data.data[WL_STATUS_BT]);
+ if (ret)
+ goto exit;
+
+exit:
+ if (ret)
+ samsung_rfkill_exit(samsung);
+
+ return ret;
+}
+
+static int __init samsung_rfkill_init(struct samsung_laptop *samsung)
+{
+ if (samsung->config->sabi_version == 2)
+ return samsung_rfkill_init_seclinux(samsung);
+ if (samsung->config->sabi_version == 3)
+ return samsung_rfkill_init_swsmi(samsung);
+ return 0;
+}
+
+static int kbd_backlight_enable(struct samsung_laptop *samsung)
+{
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
int retval;
- mutex_init(&sabi_mutex);
+ if (commands->kbd_backlight == 0xFFFF)
+ return -ENODEV;
+
+ memset(&data, 0, sizeof(data));
+ data.d0 = 0xaabb;
+ retval = sabi_command(samsung, commands->kbd_backlight,
+ &data, &data);
- if (!force && !dmi_check_system(samsung_dmi_table))
+ if (retval)
+ return retval;
+
+ if (data.d0 != 0xccdd)
return -ENODEV;
+ return 0;
+}
- f0000_segment = ioremap_nocache(0xf0000, 0xffff);
- if (!f0000_segment) {
- pr_err("Can't map the segment at 0xf0000\n");
- return -EINVAL;
+static int kbd_backlight_read(struct samsung_laptop *samsung)
+{
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+ int retval;
+
+ memset(&data, 0, sizeof(data));
+ data.data[0] = 0x81;
+ retval = sabi_command(samsung, commands->kbd_backlight,
+ &data, &data);
+
+ if (retval)
+ return retval;
+
+ return data.data[0];
+}
+
+static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness)
+{
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+
+ memset(&data, 0, sizeof(data));
+ data.d0 = 0x82 | ((brightness & 0xFF) << 8);
+ return sabi_command(samsung, commands->kbd_backlight,
+ &data, NULL);
+}
+
+static void kbd_led_update(struct work_struct *work)
+{
+ struct samsung_laptop *samsung;
+
+ samsung = container_of(work, struct samsung_laptop, kbd_led_work);
+ kbd_backlight_write(samsung, samsung->kbd_led_wk);
+}
+
+static void kbd_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct samsung_laptop *samsung;
+
+ samsung = container_of(led_cdev, struct samsung_laptop, kbd_led);
+
+ if (value > samsung->kbd_led.max_brightness)
+ value = samsung->kbd_led.max_brightness;
+ else if (value < 0)
+ value = 0;
+
+ samsung->kbd_led_wk = value;
+ queue_work(samsung->led_workqueue, &samsung->kbd_led_work);
+}
+
+static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
+{
+ struct samsung_laptop *samsung;
+
+ samsung = container_of(led_cdev, struct samsung_laptop, kbd_led);
+ return kbd_backlight_read(samsung);
+}
+
+static void samsung_leds_exit(struct samsung_laptop *samsung)
+{
+ if (!IS_ERR_OR_NULL(samsung->kbd_led.dev))
+ led_classdev_unregister(&samsung->kbd_led);
+ if (samsung->led_workqueue)
+ destroy_workqueue(samsung->led_workqueue);
+}
+
+static int __init samsung_leds_init(struct samsung_laptop *samsung)
+{
+ int ret = 0;
+
+ samsung->led_workqueue = create_singlethread_workqueue("led_workqueue");
+ if (!samsung->led_workqueue)
+ return -ENOMEM;
+
+ if (kbd_backlight_enable(samsung) >= 0) {
+ INIT_WORK(&samsung->kbd_led_work, kbd_led_update);
+
+ samsung->kbd_led.name = "samsung::kbd_backlight";
+ samsung->kbd_led.brightness_set = kbd_led_set;
+ samsung->kbd_led.brightness_get = kbd_led_get;
+ samsung->kbd_led.max_brightness = 8;
+
+ ret = led_classdev_register(&samsung->platform_device->dev,
+ &samsung->kbd_led);
+ }
+
+ if (ret)
+ samsung_leds_exit(samsung);
+
+ return ret;
+}
+
+static void samsung_backlight_exit(struct samsung_laptop *samsung)
+{
+ if (samsung->backlight_device) {
+ backlight_device_unregister(samsung->backlight_device);
+ samsung->backlight_device = NULL;
+ }
+}
+
+static int __init samsung_backlight_init(struct samsung_laptop *samsung)
+{
+ struct backlight_device *bd;
+ struct backlight_properties props;
+
+ if (!samsung->handle_backlight)
+ return 0;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = samsung->config->max_brightness -
+ samsung->config->min_brightness;
+
+ bd = backlight_device_register("samsung",
+ &samsung->platform_device->dev,
+ samsung, &backlight_ops,
+ &props);
+ if (IS_ERR(bd))
+ return PTR_ERR(bd);
+
+ samsung->backlight_device = bd;
+ samsung->backlight_device->props.brightness = read_brightness(samsung);
+ samsung->backlight_device->props.power = FB_BLANK_UNBLANK;
+ backlight_update_status(samsung->backlight_device);
+
+ return 0;
+}
+
+static umode_t samsung_sysfs_is_visible(struct kobject *kobj,
+ struct attribute *attr, int idx)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct samsung_laptop *samsung = platform_get_drvdata(pdev);
+ bool ok = true;
+
+ if (attr == &dev_attr_performance_level.attr)
+ ok = !!samsung->config->performance_levels[0].name;
+ if (attr == &dev_attr_battery_life_extender.attr)
+ ok = !!(read_battery_life_extender(samsung) >= 0);
+ if (attr == &dev_attr_usb_charge.attr)
+ ok = !!(read_usb_charge(samsung) >= 0);
+
+ return ok ? attr->mode : 0;
+}
+
+static struct attribute_group platform_attribute_group = {
+ .is_visible = samsung_sysfs_is_visible,
+ .attrs = platform_attributes
+};
+
+static void samsung_sysfs_exit(struct samsung_laptop *samsung)
+{
+ struct platform_device *device = samsung->platform_device;
+
+ sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
+}
+
+static int __init samsung_sysfs_init(struct samsung_laptop *samsung)
+{
+ struct platform_device *device = samsung->platform_device;
+
+ return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
+
+}
+
+static int show_call(struct seq_file *m, void *data)
+{
+ struct samsung_laptop *samsung = m->private;
+ struct sabi_data *sdata = &samsung->debug.data;
+ int ret;
+
+ seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
+ samsung->debug.command,
+ sdata->d0, sdata->d1, sdata->d2, sdata->d3);
+
+ ret = sabi_command(samsung, samsung->debug.command, sdata, sdata);
+
+ if (ret) {
+ seq_printf(m, "SABI command 0x%04x failed\n",
+ samsung->debug.command);
+ return ret;
+ }
+
+ seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
+ sdata->d0, sdata->d1, sdata->d2, sdata->d3);
+ return 0;
+}
+
+static int samsung_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, show_call, inode->i_private);
+}
+
+static const struct file_operations samsung_laptop_call_io_ops = {
+ .owner = THIS_MODULE,
+ .open = samsung_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void samsung_debugfs_exit(struct samsung_laptop *samsung)
+{
+ debugfs_remove_recursive(samsung->debug.root);
+}
+
+static int samsung_debugfs_init(struct samsung_laptop *samsung)
+{
+ struct dentry *dent;
+
+ samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL);
+ if (!samsung->debug.root) {
+ pr_err("failed to create debugfs directory");
+ goto error_debugfs;
+ }
+
+ samsung->debug.f0000_wrapper.data = samsung->f0000_segment;
+ samsung->debug.f0000_wrapper.size = 0xffff;
+
+ samsung->debug.data_wrapper.data = &samsung->debug.data;
+ samsung->debug.data_wrapper.size = sizeof(samsung->debug.data);
+
+ samsung->debug.sdiag_wrapper.data = samsung->sdiag;
+ samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag);
+
+ dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR,
+ samsung->debug.root, &samsung->debug.command);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root,
+ &samsung->debug.data.d0);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root,
+ &samsung->debug.data.d1);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root,
+ &samsung->debug.data.d2);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root,
+ &samsung->debug.data.d3);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR,
+ samsung->debug.root,
+ &samsung->debug.data_wrapper);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR,
+ samsung->debug.root,
+ &samsung->debug.f0000_wrapper);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_file("call", S_IFREG | S_IRUGO,
+ samsung->debug.root, samsung,
+ &samsung_laptop_call_io_ops);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR,
+ samsung->debug.root,
+ &samsung->debug.sdiag_wrapper);
+ if (!dent)
+ goto error_debugfs;
+
+ return 0;
+
+error_debugfs:
+ samsung_debugfs_exit(samsung);
+ return -ENOMEM;
+}
+
+static void samsung_sabi_exit(struct samsung_laptop *samsung)
+{
+ const struct sabi_config *config = samsung->config;
+
+ /* Turn off "Linux" mode in the BIOS */
+ if (config && config->commands.set_linux != 0xff)
+ sabi_set_commandb(samsung, config->commands.set_linux, 0x80);
+
+ if (samsung->sabi_iface) {
+ iounmap(samsung->sabi_iface);
+ samsung->sabi_iface = NULL;
+ }
+ if (samsung->f0000_segment) {
+ iounmap(samsung->f0000_segment);
+ samsung->f0000_segment = NULL;
+ }
+
+ samsung->config = NULL;
+}
+
+static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca,
+ unsigned int ifaceP)
+{
+ const struct sabi_config *config = samsung->config;
+
+ printk(KERN_DEBUG "This computer supports SABI==%x\n",
+ loca + 0xf0000 - 6);
+
+ printk(KERN_DEBUG "SABI header:\n");
+ printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
+ readw(samsung->sabi + config->header_offsets.port));
+ printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
+ readb(samsung->sabi + config->header_offsets.iface_func));
+ printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
+ readb(samsung->sabi + config->header_offsets.en_mem));
+ printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
+ readb(samsung->sabi + config->header_offsets.re_mem));
+ printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
+ readw(samsung->sabi + config->header_offsets.data_offset));
+ printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
+ readw(samsung->sabi + config->header_offsets.data_segment));
+
+ printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP);
+}
+
+static void __init samsung_sabi_diag(struct samsung_laptop *samsung)
+{
+ int loca = find_signature(samsung->f0000_segment, "SDiaG@");
+ int i;
+
+ if (loca == 0xffff)
+ return ;
+
+ /* Example:
+ * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A
+ *
+ * Product name: 90X3A
+ * BIOS Version: 07HL
+ */
+ loca += 1;
+ for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) {
+ char temp = readb(samsung->f0000_segment + loca);
+
+ if (isalnum(temp) || temp == '/' || temp == '-')
+ samsung->sdiag[i++] = temp;
+ else
+ break ;
}
+ if (debug && samsung->sdiag[0])
+ pr_info("sdiag: %s", samsung->sdiag);
+}
+
+static int __init samsung_sabi_init(struct samsung_laptop *samsung)
+{
+ const struct sabi_config *config = NULL;
+ const struct sabi_commands *commands;
+ unsigned int ifaceP;
+ int ret = 0;
+ int i;
+ int loca;
+
+ samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff);
+ if (!samsung->f0000_segment) {
+ if (debug || force)
+ pr_err("Can't map the segment at 0xf0000\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ samsung_sabi_diag(samsung);
+
/* Try to find one of the signatures in memory to find the header */
for (i = 0; sabi_configs[i].test_string != 0; ++i) {
- sabi_config = &sabi_configs[i];
- loca = find_signature(f0000_segment, sabi_config->test_string);
+ samsung->config = &sabi_configs[i];
+ loca = find_signature(samsung->f0000_segment,
+ samsung->config->test_string);
if (loca != 0xffff)
break;
}
if (loca == 0xffff) {
- pr_err("This computer does not support SABI\n");
- goto error_no_signature;
+ if (debug || force)
+ pr_err("This computer does not support SABI\n");
+ ret = -ENODEV;
+ goto exit;
}
+ config = samsung->config;
+ commands = &config->commands;
+
/* point to the SMI port Number */
loca += 1;
- sabi = (f0000_segment + loca);
-
- if (debug) {
- printk(KERN_DEBUG "This computer supports SABI==%x\n",
- loca + 0xf0000 - 6);
- printk(KERN_DEBUG "SABI header:\n");
- printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
- readw(sabi + sabi_config->header_offsets.port));
- printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
- readb(sabi + sabi_config->header_offsets.iface_func));
- printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
- readb(sabi + sabi_config->header_offsets.en_mem));
- printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
- readb(sabi + sabi_config->header_offsets.re_mem));
- printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
- readw(sabi + sabi_config->header_offsets.data_offset));
- printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
- readw(sabi + sabi_config->header_offsets.data_segment));
- }
+ samsung->sabi = (samsung->f0000_segment + loca);
/* Get a pointer to the SABI Interface */
- ifaceP = (readw(sabi + sabi_config->header_offsets.data_segment) & 0x0ffff) << 4;
- ifaceP += readw(sabi + sabi_config->header_offsets.data_offset) & 0x0ffff;
- sabi_iface = ioremap_nocache(ifaceP, 16);
- if (!sabi_iface) {
- pr_err("Can't remap %x\n", ifaceP);
- goto error_no_signature;
- }
- if (debug) {
- printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP);
- printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface);
+ ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4;
+ ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff;
- test_backlight();
- test_wireless();
+ if (debug)
+ samsung_sabi_infos(samsung, loca, ifaceP);
- retval = sabi_get_command(sabi_config->commands.get_brightness,
- &sretval);
- printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]);
+ samsung->sabi_iface = ioremap_nocache(ifaceP, 16);
+ if (!samsung->sabi_iface) {
+ pr_err("Can't remap %x\n", ifaceP);
+ ret = -EINVAL;
+ goto exit;
}
/* Turn on "Linux" mode in the BIOS */
- if (sabi_config->commands.set_linux != 0xff) {
- retval = sabi_set_command(sabi_config->commands.set_linux,
- 0x81);
+ if (commands->set_linux != 0xff) {
+ int retval = sabi_set_commandb(samsung,
+ commands->set_linux, 0x81);
if (retval) {
pr_warn("Linux mode was not set!\n");
- goto error_no_platform;
+ ret = -ENODEV;
+ goto exit;
}
}
/* Check for stepping quirk */
- check_for_stepping_quirk();
+ if (samsung->handle_backlight)
+ check_for_stepping_quirk(samsung);
- /* knock up a platform device to hang stuff off of */
- sdev = platform_device_register_simple("samsung", -1, NULL, 0);
- if (IS_ERR(sdev))
- goto error_no_platform;
+ pr_info("detected SABI interface: %s\n",
+ samsung->config->test_string);
- /* create a backlight device to talk to this one */
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_PLATFORM;
- props.max_brightness = sabi_config->max_brightness -
- sabi_config->min_brightness;
- backlight_device = backlight_device_register("samsung", &sdev->dev,
- NULL, &backlight_ops,
- &props);
- if (IS_ERR(backlight_device))
- goto error_no_backlight;
-
- backlight_device->props.brightness = read_brightness();
- backlight_device->props.power = FB_BLANK_UNBLANK;
- backlight_update_status(backlight_device);
-
- retval = init_wireless(sdev);
- if (retval)
- goto error_no_rfk;
+exit:
+ if (ret)
+ samsung_sabi_exit(samsung);
- retval = device_create_file(&sdev->dev, &dev_attr_performance_level);
- if (retval)
- goto error_file_create;
+ return ret;
+}
+
+static void samsung_platform_exit(struct samsung_laptop *samsung)
+{
+ if (samsung->platform_device) {
+ platform_device_unregister(samsung->platform_device);
+ samsung->platform_device = NULL;
+ }
+}
+
+static int __init samsung_platform_init(struct samsung_laptop *samsung)
+{
+ struct platform_device *pdev;
+
+ pdev = platform_device_register_simple("samsung", -1, NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+ samsung->platform_device = pdev;
+ platform_set_drvdata(samsung->platform_device, samsung);
return 0;
+}
+
+static struct samsung_quirks *quirks;
+
+static int __init samsung_dmi_matched(const struct dmi_system_id *d)
+{
+ quirks = d->driver_data;
+ return 0;
+}
+
+static struct dmi_system_id __initdata samsung_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
+ },
+ },
+ /* Specific DMI ids for laptop with quirks */
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "N150P",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N150P"),
+ DMI_MATCH(DMI_BOARD_NAME, "N150P"),
+ },
+ .driver_data = &samsung_broken_acpi_video,
+ },
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "N145P/N250P/N260P",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
+ DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
+ },
+ .driver_data = &samsung_broken_acpi_video,
+ },
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "N150/N210/N220",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
+ DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
+ },
+ .driver_data = &samsung_broken_acpi_video,
+ },
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "NF110/NF210/NF310",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
+ DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
+ },
+ .driver_data = &samsung_broken_acpi_video,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
-error_file_create:
- destroy_wireless();
+static struct platform_device *samsung_platform_device;
-error_no_rfk:
- backlight_device_unregister(backlight_device);
+static int __init samsung_init(void)
+{
+ struct samsung_laptop *samsung;
+ int ret;
-error_no_backlight:
- platform_device_unregister(sdev);
+ quirks = &samsung_unknown;
+ if (!force && !dmi_check_system(samsung_dmi_table))
+ return -ENODEV;
+
+ samsung = kzalloc(sizeof(*samsung), GFP_KERNEL);
+ if (!samsung)
+ return -ENOMEM;
-error_no_platform:
- iounmap(sabi_iface);
+ mutex_init(&samsung->sabi_mutex);
+ samsung->handle_backlight = true;
+ samsung->quirks = quirks;
-error_no_signature:
- iounmap(f0000_segment);
- return -EINVAL;
+
+#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
+ /* Don't handle backlight here if the acpi video already handle it */
+ if (acpi_video_backlight_support()) {
+ if (samsung->quirks->broken_acpi_video) {
+ pr_info("Disabling ACPI video driver\n");
+ acpi_video_unregister();
+ } else {
+ samsung->handle_backlight = false;
+ }
+ }
+#endif
+
+ ret = samsung_platform_init(samsung);
+ if (ret)
+ goto error_platform;
+
+ ret = samsung_sabi_init(samsung);
+ if (ret)
+ goto error_sabi;
+
+#ifdef CONFIG_ACPI
+ /* Only log that if we are really on a sabi platform */
+ if (acpi_video_backlight_support() &&
+ !samsung->quirks->broken_acpi_video)
+ pr_info("Backlight controlled by ACPI video driver\n");
+#endif
+
+ ret = samsung_sysfs_init(samsung);
+ if (ret)
+ goto error_sysfs;
+
+ ret = samsung_backlight_init(samsung);
+ if (ret)
+ goto error_backlight;
+
+ ret = samsung_rfkill_init(samsung);
+ if (ret)
+ goto error_rfkill;
+
+ ret = samsung_leds_init(samsung);
+ if (ret)
+ goto error_leds;
+
+ ret = samsung_debugfs_init(samsung);
+ if (ret)
+ goto error_debugfs;
+
+ samsung_platform_device = samsung->platform_device;
+ return ret;
+
+error_debugfs:
+ samsung_leds_exit(samsung);
+error_leds:
+ samsung_rfkill_exit(samsung);
+error_rfkill:
+ samsung_backlight_exit(samsung);
+error_backlight:
+ samsung_sysfs_exit(samsung);
+error_sysfs:
+ samsung_sabi_exit(samsung);
+error_sabi:
+ samsung_platform_exit(samsung);
+error_platform:
+ kfree(samsung);
+ return ret;
}
static void __exit samsung_exit(void)
{
- /* Turn off "Linux" mode in the BIOS */
- if (sabi_config->commands.set_linux != 0xff)
- sabi_set_command(sabi_config->commands.set_linux, 0x80);
-
- device_remove_file(&sdev->dev, &dev_attr_performance_level);
- backlight_device_unregister(backlight_device);
- destroy_wireless();
- iounmap(sabi_iface);
- iounmap(f0000_segment);
- platform_device_unregister(sdev);
+ struct samsung_laptop *samsung;
+
+ samsung = platform_get_drvdata(samsung_platform_device);
+
+ samsung_debugfs_exit(samsung);
+ samsung_leds_exit(samsung);
+ samsung_rfkill_exit(samsung);
+ samsung_backlight_exit(samsung);
+ samsung_sysfs_exit(samsung);
+ samsung_sabi_exit(samsung);
+ samsung_platform_exit(samsung);
+
+ kfree(samsung);
+ samsung_platform_device = NULL;
}
module_init(samsung_init);
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index c006dee..8a51795 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -127,7 +127,7 @@ MODULE_PARM_DESC(minor,
"default is -1 (automatic)");
#endif
-static int kbd_backlight; /* = 1 */
+static int kbd_backlight = 1;
module_param(kbd_backlight, int, 0444);
MODULE_PARM_DESC(kbd_backlight,
"set this to 0 to disable keyboard backlight, "
@@ -347,6 +347,7 @@ static void sony_laptop_report_input_event(u8 event)
struct input_dev *jog_dev = sony_laptop_input.jog_dev;
struct input_dev *key_dev = sony_laptop_input.key_dev;
struct sony_laptop_keypress kp = { NULL };
+ int scancode = -1;
if (event == SONYPI_EVENT_FNKEY_RELEASED ||
event == SONYPI_EVENT_ANYBUTTON_RELEASED) {
@@ -380,8 +381,8 @@ static void sony_laptop_report_input_event(u8 event)
dprintk("sony_laptop_report_input_event, event not known: %d\n", event);
break;
}
- if (sony_laptop_input_index[event] != -1) {
- kp.key = sony_laptop_input_keycode_map[sony_laptop_input_index[event]];
+ if ((scancode = sony_laptop_input_index[event]) != -1) {
+ kp.key = sony_laptop_input_keycode_map[scancode];
if (kp.key != KEY_UNKNOWN)
kp.dev = key_dev;
}
@@ -389,9 +390,11 @@ static void sony_laptop_report_input_event(u8 event)
}
if (kp.dev) {
+ /* if we have a scancode we emit it so we can always
+ remap the key */
+ if (scancode != -1)
+ input_event(kp.dev, EV_MSC, MSC_SCAN, scancode);
input_report_key(kp.dev, kp.key, 1);
- /* we emit the scancode so we can always remap the key */
- input_event(kp.dev, EV_MSC, MSC_SCAN, event);
input_sync(kp.dev);
/* schedule key release */
@@ -466,7 +469,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
jog_dev->name = "Sony Vaio Jogdial";
jog_dev->id.bustype = BUS_ISA;
jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
- key_dev->dev.parent = &acpi_device->dev;
+ jog_dev->dev.parent = &acpi_device->dev;
input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE);
input_set_capability(jog_dev, EV_REL, REL_WHEEL);
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index ea0c607..d68c000 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -8658,7 +8658,7 @@ static int __must_check __init get_thinkpad_model_data(
}
s = dmi_get_system_info(DMI_PRODUCT_VERSION);
- if (s && !strnicmp(s, "ThinkPad", 8)) {
+ if (s && !(strnicmp(s, "ThinkPad", 8) && strnicmp(s, "Lenovo", 6))) {
tp->model_str = kstrdup(s, GFP_KERNEL);
if (!tp->model_str)
return -ENOMEM;
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index dcdc1f4..ee79ce6 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -52,6 +52,8 @@
#include <linux/input/sparse-keymap.h>
#include <linux/leds.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/i8042.h>
#include <asm/uaccess.h>
@@ -61,6 +63,11 @@ MODULE_AUTHOR("John Belmonte");
MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
MODULE_LICENSE("GPL");
+#define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100"
+
+/* Scan code for Fn key on TOS1900 models */
+#define TOS1900_FN_SCAN 0x6e
+
/* Toshiba ACPI method paths */
#define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX"
@@ -95,6 +102,8 @@ MODULE_LICENSE("GPL");
#define HCI_WIRELESS 0x0056
/* field definitions */
+#define HCI_HOTKEY_DISABLE 0x0b
+#define HCI_HOTKEY_ENABLE 0x09
#define HCI_LCD_BRIGHTNESS_BITS 3
#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS)
@@ -111,6 +120,7 @@ struct toshiba_acpi_dev {
const char *method_hci;
struct rfkill *bt_rfk;
struct input_dev *hotkey_dev;
+ struct work_struct hotkey_work;
struct backlight_device *backlight_dev;
struct led_classdev led_dev;
@@ -118,14 +128,18 @@ struct toshiba_acpi_dev {
int last_key_event;
int key_event_valid;
- int illumination_supported:1;
- int video_supported:1;
- int fan_supported:1;
- int system_event_supported:1;
+ unsigned int illumination_supported:1;
+ unsigned int video_supported:1;
+ unsigned int fan_supported:1;
+ unsigned int system_event_supported:1;
+ unsigned int ntfy_supported:1;
+ unsigned int info_supported:1;
struct mutex mutex;
};
+static struct toshiba_acpi_dev *toshiba_acpi;
+
static const struct acpi_device_id toshiba_device_ids[] = {
{"TOS6200", 0},
{"TOS6208", 0},
@@ -138,6 +152,8 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = {
{ KE_KEY, 0x101, { KEY_MUTE } },
{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
{ KE_KEY, 0x103, { KEY_ZOOMIN } },
+ { KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } },
+ { KE_KEY, 0x139, { KEY_ZOOMRESET } },
{ KE_KEY, 0x13b, { KEY_COFFEE } },
{ KE_KEY, 0x13c, { KEY_BATTERY } },
{ KE_KEY, 0x13d, { KEY_SLEEP } },
@@ -146,7 +162,7 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = {
{ KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0x142, { KEY_WLAN } },
- { KE_KEY, 0x143, { KEY_PROG1 } },
+ { KE_KEY, 0x143, { KEY_TOUCHPAD_TOGGLE } },
{ KE_KEY, 0x17f, { KEY_FN } },
{ KE_KEY, 0xb05, { KEY_PROG2 } },
{ KE_KEY, 0xb06, { KEY_WWW } },
@@ -156,6 +172,7 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = {
{ KE_KEY, 0xb32, { KEY_NEXTSONG } },
{ KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
{ KE_KEY, 0xb5a, { KEY_MEDIA } },
+ { KE_IGNORE, 0x1430, { KEY_RESERVED } },
{ KE_END, 0 },
};
@@ -847,10 +864,78 @@ static const struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status,
};
+static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
+ struct serio *port)
+{
+ if (str & 0x20)
+ return false;
+
+ if (unlikely(data == 0xe0))
+ return false;
+
+ if ((data & 0x7f) == TOS1900_FN_SCAN) {
+ schedule_work(&toshiba_acpi->hotkey_work);
+ return true;
+ }
+
+ return false;
+}
+
+static void toshiba_acpi_hotkey_work(struct work_struct *work)
+{
+ acpi_handle ec_handle = ec_get_handle();
+ acpi_status status;
+
+ if (!ec_handle)
+ return;
+
+ status = acpi_evaluate_object(ec_handle, "NTFY", NULL, NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("ACPI NTFY method execution failed\n");
+}
+
+/*
+ * Returns hotkey scancode, or < 0 on failure.
+ */
+static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev)
+{
+ struct acpi_buffer buf;
+ union acpi_object out_obj;
+ acpi_status status;
+
+ buf.pointer = &out_obj;
+ buf.length = sizeof(out_obj);
+
+ status = acpi_evaluate_object(dev->acpi_dev->handle, "INFO",
+ NULL, &buf);
+ if (ACPI_FAILURE(status) || out_obj.type != ACPI_TYPE_INTEGER) {
+ pr_err("ACPI INFO method execution failed\n");
+ return -EIO;
+ }
+
+ return out_obj.integer.value;
+}
+
+static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,
+ int scancode)
+{
+ if (scancode == 0x100)
+ return;
+
+ /* act on key press; ignore key release */
+ if (scancode & 0x80)
+ return;
+
+ if (!sparse_keymap_report_event(dev->hotkey_dev, scancode, 1, true))
+ pr_info("Unknown key %x\n", scancode);
+}
+
static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
{
acpi_status status;
+ acpi_handle ec_handle, handle;
int error;
+ u32 hci_result;
dev->hotkey_dev = input_allocate_device();
if (!dev->hotkey_dev) {
@@ -866,21 +951,67 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
if (error)
goto err_free_dev;
+ /*
+ * For some machines the SCI responsible for providing hotkey
+ * notification doesn't fire. We can trigger the notification
+ * whenever the Fn key is pressed using the NTFY method, if
+ * supported, so if it's present set up an i8042 key filter
+ * for this purpose.
+ */
+ status = AE_ERROR;
+ ec_handle = ec_get_handle();
+ if (ec_handle)
+ status = acpi_get_handle(ec_handle, "NTFY", &handle);
+
+ if (ACPI_SUCCESS(status)) {
+ INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
+
+ error = i8042_install_filter(toshiba_acpi_i8042_filter);
+ if (error) {
+ pr_err("Error installing key filter\n");
+ goto err_free_keymap;
+ }
+
+ dev->ntfy_supported = 1;
+ }
+
+ /*
+ * Determine hotkey query interface. Prefer using the INFO
+ * method when it is available.
+ */
+ status = acpi_get_handle(dev->acpi_dev->handle, "INFO", &handle);
+ if (ACPI_SUCCESS(status)) {
+ dev->info_supported = 1;
+ } else {
+ hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
+ if (hci_result == HCI_SUCCESS)
+ dev->system_event_supported = 1;
+ }
+
+ if (!dev->info_supported && !dev->system_event_supported) {
+ pr_warn("No hotkey query interface found\n");
+ goto err_remove_filter;
+ }
+
status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL);
if (ACPI_FAILURE(status)) {
pr_info("Unable to enable hotkeys\n");
error = -ENODEV;
- goto err_free_keymap;
+ goto err_remove_filter;
}
error = input_register_device(dev->hotkey_dev);
if (error) {
pr_info("Unable to register input device\n");
- goto err_free_keymap;
+ goto err_remove_filter;
}
+ hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &hci_result);
return 0;
+ err_remove_filter:
+ if (dev->ntfy_supported)
+ i8042_remove_filter(toshiba_acpi_i8042_filter);
err_free_keymap:
sparse_keymap_free(dev->hotkey_dev);
err_free_dev:
@@ -895,6 +1026,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)
remove_toshiba_proc_entries(dev);
+ if (dev->ntfy_supported) {
+ i8042_remove_filter(toshiba_acpi_i8042_filter);
+ cancel_work_sync(&dev->hotkey_work);
+ }
+
if (dev->hotkey_dev) {
input_unregister_device(dev->hotkey_dev);
sparse_keymap_free(dev->hotkey_dev);
@@ -911,6 +1047,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)
if (dev->illumination_supported)
led_classdev_unregister(&dev->led_dev);
+ if (toshiba_acpi)
+ toshiba_acpi = NULL;
+
kfree(dev);
return 0;
@@ -936,12 +1075,14 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
{
struct toshiba_acpi_dev *dev;
const char *hci_method;
- u32 hci_result;
u32 dummy;
bool bt_present;
int ret = 0;
struct backlight_properties props;
+ if (toshiba_acpi)
+ return -EBUSY;
+
pr_info("Toshiba Laptop ACPI Extras version %s\n",
TOSHIBA_ACPI_VERSION);
@@ -963,11 +1104,6 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
mutex_init(&dev->mutex);
- /* enable event fifo */
- hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
- if (hci_result == HCI_SUCCESS)
- dev->system_event_supported = 1;
-
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
dev->backlight_dev = backlight_device_register("toshiba",
@@ -1024,6 +1160,8 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
create_toshiba_proc_entries(dev);
+ toshiba_acpi = dev;
+
return 0;
error:
@@ -1036,40 +1174,64 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
u32 hci_result, value;
int retries = 3;
+ int scancode;
- if (!dev->system_event_supported || event != 0x80)
+ if (event != 0x80)
return;
- do {
- hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
- switch (hci_result) {
- case HCI_SUCCESS:
- if (value == 0x100)
- continue;
- /* act on key press; ignore key release */
- if (value & 0x80)
- continue;
-
- if (!sparse_keymap_report_event(dev->hotkey_dev,
- value, 1, true)) {
- pr_info("Unknown key %x\n",
- value);
+ if (dev->info_supported) {
+ scancode = toshiba_acpi_query_hotkey(dev);
+ if (scancode < 0)
+ pr_err("Failed to query hotkey event\n");
+ else if (scancode != 0)
+ toshiba_acpi_report_hotkey(dev, scancode);
+ } else if (dev->system_event_supported) {
+ do {
+ hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
+ switch (hci_result) {
+ case HCI_SUCCESS:
+ toshiba_acpi_report_hotkey(dev, (int)value);
+ break;
+ case HCI_NOT_SUPPORTED:
+ /*
+ * This is a workaround for an unresolved
+ * issue on some machines where system events
+ * sporadically become disabled.
+ */
+ hci_write1(dev, HCI_SYSTEM_EVENT, 1,
+ &hci_result);
+ pr_notice("Re-enabled hotkeys\n");
+ /* fall through */
+ default:
+ retries--;
+ break;
}
- break;
- case HCI_NOT_SUPPORTED:
- /* This is a workaround for an unresolved issue on
- * some machines where system events sporadically
- * become disabled. */
- hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
- pr_notice("Re-enabled hotkeys\n");
- /* fall through */
- default:
- retries--;
- break;
- }
- } while (retries && hci_result != HCI_EMPTY);
+ } while (retries && hci_result != HCI_EMPTY);
+ }
+}
+
+static int toshiba_acpi_suspend(struct acpi_device *acpi_dev,
+ pm_message_t state)
+{
+ struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
+ u32 result;
+
+ if (dev->hotkey_dev)
+ hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE, &result);
+
+ return 0;
}
+static int toshiba_acpi_resume(struct acpi_device *acpi_dev)
+{
+ struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
+ u32 result;
+
+ if (dev->hotkey_dev)
+ hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &result);
+
+ return 0;
+}
static struct acpi_driver toshiba_acpi_driver = {
.name = "Toshiba ACPI driver",
@@ -1080,6 +1242,8 @@ static struct acpi_driver toshiba_acpi_driver = {
.add = toshiba_acpi_add,
.remove = toshiba_acpi_remove,
.notify = toshiba_acpi_notify,
+ .suspend = toshiba_acpi_suspend,
+ .resume = toshiba_acpi_resume,
},
};
@@ -1087,6 +1251,14 @@ static int __init toshiba_acpi_init(void)
{
int ret;
+ /*
+ * Machines with this WMI guid aren't supported due to bugs in
+ * their AML. This check relies on wmi initializing before
+ * toshiba_acpi to guarantee guids have been identified.
+ */
+ if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
+ return -ENODEV;
+
toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
if (!toshiba_proc_dir) {
pr_err("Unable to create proc dir " PROC_TOSHIBA "\n");
diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c
index e549eee..41781ed 100644
--- a/drivers/platform/x86/xo1-rfkill.c
+++ b/drivers/platform/x86/xo1-rfkill.c
@@ -67,19 +67,8 @@ static struct platform_driver xo1_rfkill_driver = {
.remove = __devexit_p(xo1_rfkill_remove),
};
-static int __init xo1_rfkill_init(void)
-{
- return platform_driver_register(&xo1_rfkill_driver);
-}
-
-static void __exit xo1_rfkill_exit(void)
-{
- platform_driver_unregister(&xo1_rfkill_driver);
-}
+module_platform_driver(xo1_rfkill_driver);
MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:xo1-rfkill");
-
-module_init(xo1_rfkill_init);
-module_exit(xo1_rfkill_exit);
diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c
index b00c176..d21e8f5 100644
--- a/drivers/pnp/pnpacpi/core.c
+++ b/drivers/pnp/pnpacpi/core.c
@@ -321,9 +321,14 @@ static int __init acpi_pnp_match(struct device *dev, void *_pnp)
{
struct acpi_device *acpi = to_acpi_device(dev);
struct pnp_dev *pnp = _pnp;
+ struct device *physical_device;
+
+ physical_device = acpi_get_physical_device(acpi->handle);
+ if (physical_device)
+ put_device(physical_device);
/* true means it matched */
- return !acpi_get_physical_device(acpi->handle)
+ return !physical_device
&& compare_pnp_id(pnp->id, acpi_device_hid(acpi));
}
diff --git a/drivers/pnp/pnpbios/bioscalls.c b/drivers/pnp/pnpbios/bioscalls.c
index b859d16..769d265 100644
--- a/drivers/pnp/pnpbios/bioscalls.c
+++ b/drivers/pnp/pnpbios/bioscalls.c
@@ -17,7 +17,6 @@
#include <asm/page.h>
#include <asm/desc.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
#include "pnpbios.h"
diff --git a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c
index cfe8685..9d42226 100644
--- a/drivers/pnp/pnpbios/core.c
+++ b/drivers/pnp/pnpbios/core.c
@@ -65,7 +65,6 @@
#include <asm/page.h>
#include <asm/desc.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
#include "../base.h"
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 459f664..99dc29f 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -249,7 +249,7 @@ config CHARGER_TWL4030
Say Y here to enable support for TWL4030 Battery Charge Interface.
config CHARGER_LP8727
- tristate "National Semiconductor LP8727 charger driver"
+ tristate "TI/National Semiconductor LP8727 charger driver"
depends on I2C
help
Say Y here to enable support for LP8727 Charger Driver.
@@ -288,4 +288,23 @@ config CHARGER_MAX8998
Say Y to enable support for the battery charger control sysfs and
platform data of MAX8998/LP3974 PMICs.
+config CHARGER_SMB347
+ tristate "Summit Microelectronics SMB347 Battery Charger"
+ depends on I2C
+ help
+ Say Y to include support for Summit Microelectronics SMB347
+ Battery Charger.
+
+config AB8500_BM
+ bool "AB8500 Battery Management Driver"
+ depends on AB8500_CORE && AB8500_GPADC
+ help
+ Say Y to include support for AB5500 battery management.
+
+config AB8500_BATTERY_THERM_ON_BATCTRL
+ bool "Thermistor connected on BATCTRL ADC"
+ depends on AB8500_BM
+ help
+ Say Y to enable battery temperature measurements using
+ thermistor connected on BATCTRL ADC.
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index c590fa5..b6b2434 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
+obj-$(CONFIG_AB8500_BM) += ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
@@ -42,3 +43,4 @@ obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
+obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
new file mode 100644
index 0000000..d8bb993
--- /dev/null
+++ b/drivers/power/ab8500_btemp.c
@@ -0,0 +1,1124 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Battery temperature driver for AB8500
+ *
+ * License Terms: GNU General Public License v2
+ * Author:
+ * Johan Palsson <johan.palsson@stericsson.com>
+ * Karl Komierowski <karl.komierowski@stericsson.com>
+ * Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/jiffies.h>
+
+#define VTVOUT_V 1800
+
+#define BTEMP_THERMAL_LOW_LIMIT -10
+#define BTEMP_THERMAL_MED_LIMIT 0
+#define BTEMP_THERMAL_HIGH_LIMIT_52 52
+#define BTEMP_THERMAL_HIGH_LIMIT_57 57
+#define BTEMP_THERMAL_HIGH_LIMIT_62 62
+
+#define BTEMP_BATCTRL_CURR_SRC_7UA 7
+#define BTEMP_BATCTRL_CURR_SRC_20UA 20
+
+#define to_ab8500_btemp_device_info(x) container_of((x), \
+ struct ab8500_btemp, btemp_psy);
+
+/**
+ * struct ab8500_btemp_interrupts - ab8500 interrupts
+ * @name: name of the interrupt
+ * @isr function pointer to the isr
+ */
+struct ab8500_btemp_interrupts {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct ab8500_btemp_events {
+ bool batt_rem;
+ bool btemp_high;
+ bool btemp_medhigh;
+ bool btemp_lowmed;
+ bool btemp_low;
+ bool ac_conn;
+ bool usb_conn;
+};
+
+struct ab8500_btemp_ranges {
+ int btemp_high_limit;
+ int btemp_med_limit;
+ int btemp_low_limit;
+};
+
+/**
+ * struct ab8500_btemp - ab8500 BTEMP device information
+ * @dev: Pointer to the structure device
+ * @node: List of AB8500 BTEMPs, hence prepared for reentrance
+ * @curr_source: What current source we use, in uA
+ * @bat_temp: Battery temperature in degree Celcius
+ * @prev_bat_temp Last dispatched battery temperature
+ * @parent: Pointer to the struct ab8500
+ * @gpadc: Pointer to the struct gpadc
+ * @fg: Pointer to the struct fg
+ * @pdata: Pointer to the abx500_btemp platform data
+ * @bat: Pointer to the abx500_bm platform data
+ * @btemp_psy: Structure for BTEMP specific battery properties
+ * @events: Structure for information about events triggered
+ * @btemp_ranges: Battery temperature range structure
+ * @btemp_wq: Work queue for measuring the temperature periodically
+ * @btemp_periodic_work: Work for measuring the temperature periodically
+ */
+struct ab8500_btemp {
+ struct device *dev;
+ struct list_head node;
+ int curr_source;
+ int bat_temp;
+ int prev_bat_temp;
+ struct ab8500 *parent;
+ struct ab8500_gpadc *gpadc;
+ struct ab8500_fg *fg;
+ struct abx500_btemp_platform_data *pdata;
+ struct abx500_bm_data *bat;
+ struct power_supply btemp_psy;
+ struct ab8500_btemp_events events;
+ struct ab8500_btemp_ranges btemp_ranges;
+ struct workqueue_struct *btemp_wq;
+ struct delayed_work btemp_periodic_work;
+};
+
+/* BTEMP power supply properties */
+static enum power_supply_property ab8500_btemp_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_TEMP,
+};
+
+static LIST_HEAD(ab8500_btemp_list);
+
+/**
+ * ab8500_btemp_get() - returns a reference to the primary AB8500 BTEMP
+ * (i.e. the first BTEMP in the instance list)
+ */
+struct ab8500_btemp *ab8500_btemp_get(void)
+{
+ struct ab8500_btemp *btemp;
+ btemp = list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node);
+
+ return btemp;
+}
+
+/**
+ * ab8500_btemp_batctrl_volt_to_res() - convert batctrl voltage to resistance
+ * @di: pointer to the ab8500_btemp structure
+ * @v_batctrl: measured batctrl voltage
+ * @inst_curr: measured instant current
+ *
+ * This function returns the battery resistance that is
+ * derived from the BATCTRL voltage.
+ * Returns value in Ohms.
+ */
+static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
+ int v_batctrl, int inst_curr)
+{
+ int rbs;
+
+ if (is_ab8500_1p1_or_earlier(di->parent)) {
+ /*
+ * For ABB cut1.0 and 1.1 BAT_CTRL is internally
+ * connected to 1.8V through a 450k resistor
+ */
+ return (450000 * (v_batctrl)) / (1800 - v_batctrl);
+ }
+
+ if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL) {
+ /*
+ * If the battery has internal NTC, we use the current
+ * source to calculate the resistance, 7uA or 20uA
+ */
+ rbs = (v_batctrl * 1000
+ - di->bat->gnd_lift_resistance * inst_curr)
+ / di->curr_source;
+ } else {
+ /*
+ * BAT_CTRL is internally
+ * connected to 1.8V through a 80k resistor
+ */
+ rbs = (80000 * (v_batctrl)) / (1800 - v_batctrl);
+ }
+
+ return rbs;
+}
+
+/**
+ * ab8500_btemp_read_batctrl_voltage() - measure batctrl voltage
+ * @di: pointer to the ab8500_btemp structure
+ *
+ * This function returns the voltage on BATCTRL. Returns value in mV.
+ */
+static int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di)
+{
+ int vbtemp;
+ static int prev;
+
+ vbtemp = ab8500_gpadc_convert(di->gpadc, BAT_CTRL);
+ if (vbtemp < 0) {
+ dev_err(di->dev,
+ "%s gpadc conversion failed, using previous value",
+ __func__);
+ return prev;
+ }
+ prev = vbtemp;
+ return vbtemp;
+}
+
+/**
+ * ab8500_btemp_curr_source_enable() - enable/disable batctrl current source
+ * @di: pointer to the ab8500_btemp structure
+ * @enable: enable or disable the current source
+ *
+ * Enable or disable the current sources for the BatCtrl AD channel
+ */
+static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
+ bool enable)
+{
+ int curr;
+ int ret = 0;
+
+ /*
+ * BATCTRL current sources are included on AB8500 cut2.0
+ * and future versions
+ */
+ if (is_ab8500_1p1_or_earlier(di->parent))
+ return 0;
+
+ /* Only do this for batteries with internal NTC */
+ if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
+ if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
+ curr = BAT_CTRL_7U_ENA;
+ else
+ curr = BAT_CTRL_20U_ENA;
+
+ dev_dbg(di->dev, "Set BATCTRL %duA\n", di->curr_source);
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ FORCE_BAT_CTRL_CMP_HIGH, FORCE_BAT_CTRL_CMP_HIGH);
+ if (ret) {
+ dev_err(di->dev, "%s failed setting cmp_force\n",
+ __func__);
+ return ret;
+ }
+
+ /*
+ * We have to wait one 32kHz cycle before enabling
+ * the current source, since ForceBatCtrlCmpHigh needs
+ * to be written in a separate cycle
+ */
+ udelay(32);
+
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ FORCE_BAT_CTRL_CMP_HIGH | curr);
+ if (ret) {
+ dev_err(di->dev, "%s failed enabling current source\n",
+ __func__);
+ goto disable_curr_source;
+ }
+ } else if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
+ dev_dbg(di->dev, "Disable BATCTRL curr source\n");
+
+ /* Write 0 to the curr bits */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
+ ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+ if (ret) {
+ dev_err(di->dev, "%s failed disabling current source\n",
+ __func__);
+ goto disable_curr_source;
+ }
+
+ /* Enable Pull-Up and comparator */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA,
+ BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA);
+ if (ret) {
+ dev_err(di->dev, "%s failed enabling PU and comp\n",
+ __func__);
+ goto enable_pu_comp;
+ }
+
+ /*
+ * We have to wait one 32kHz cycle before disabling
+ * ForceBatCtrlCmpHigh since this needs to be written
+ * in a separate cycle
+ */
+ udelay(32);
+
+ /* Disable 'force comparator' */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ FORCE_BAT_CTRL_CMP_HIGH, ~FORCE_BAT_CTRL_CMP_HIGH);
+ if (ret) {
+ dev_err(di->dev, "%s failed disabling force comp\n",
+ __func__);
+ goto disable_force_comp;
+ }
+ }
+ return ret;
+
+ /*
+ * We have to try unsetting FORCE_BAT_CTRL_CMP_HIGH one more time
+ * if we got an error above
+ */
+disable_curr_source:
+ /* Write 0 to the curr bits */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
+ ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+ if (ret) {
+ dev_err(di->dev, "%s failed disabling current source\n",
+ __func__);
+ return ret;
+ }
+enable_pu_comp:
+ /* Enable Pull-Up and comparator */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA,
+ BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA);
+ if (ret) {
+ dev_err(di->dev, "%s failed enabling PU and comp\n",
+ __func__);
+ return ret;
+ }
+
+disable_force_comp:
+ /*
+ * We have to wait one 32kHz cycle before disabling
+ * ForceBatCtrlCmpHigh since this needs to be written
+ * in a separate cycle
+ */
+ udelay(32);
+
+ /* Disable 'force comparator' */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+ FORCE_BAT_CTRL_CMP_HIGH, ~FORCE_BAT_CTRL_CMP_HIGH);
+ if (ret) {
+ dev_err(di->dev, "%s failed disabling force comp\n",
+ __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * ab8500_btemp_get_batctrl_res() - get battery resistance
+ * @di: pointer to the ab8500_btemp structure
+ *
+ * This function returns the battery pack identification resistance.
+ * Returns value in Ohms.
+ */
+static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
+{
+ int ret;
+ int batctrl = 0;
+ int res;
+ int inst_curr;
+ int i;
+
+ /*
+ * BATCTRL current sources are included on AB8500 cut2.0
+ * and future versions
+ */
+ ret = ab8500_btemp_curr_source_enable(di, true);
+ if (ret) {
+ dev_err(di->dev, "%s curr source enabled failed\n", __func__);
+ return ret;
+ }
+
+ if (!di->fg)
+ di->fg = ab8500_fg_get();
+ if (!di->fg) {
+ dev_err(di->dev, "No fg found\n");
+ return -EINVAL;
+ }
+
+ ret = ab8500_fg_inst_curr_start(di->fg);
+
+ if (ret) {
+ dev_err(di->dev, "Failed to start current measurement\n");
+ return ret;
+ }
+
+ /*
+ * Since there is no interrupt when current measurement is done,
+ * loop for over 250ms (250ms is one sample conversion time
+ * with 32.768 Khz RTC clock). Note that a stop time must be set
+ * since the ab8500_btemp_read_batctrl_voltage call can block and
+ * take an unknown amount of time to complete.
+ */
+ i = 0;
+
+ do {
+ batctrl += ab8500_btemp_read_batctrl_voltage(di);
+ i++;
+ msleep(20);
+ } while (!ab8500_fg_inst_curr_done(di->fg));
+ batctrl /= i;
+
+ ret = ab8500_fg_inst_curr_finalize(di->fg, &inst_curr);
+ if (ret) {
+ dev_err(di->dev, "Failed to finalize current measurement\n");
+ return ret;
+ }
+
+ res = ab8500_btemp_batctrl_volt_to_res(di, batctrl, inst_curr);
+
+ ret = ab8500_btemp_curr_source_enable(di, false);
+ if (ret) {
+ dev_err(di->dev, "%s curr source disable failed\n", __func__);
+ return ret;
+ }
+
+ dev_dbg(di->dev, "%s batctrl: %d res: %d inst_curr: %d samples: %d\n",
+ __func__, batctrl, res, inst_curr, i);
+
+ return res;
+}
+
+/**
+ * ab8500_btemp_res_to_temp() - resistance to temperature
+ * @di: pointer to the ab8500_btemp structure
+ * @tbl: pointer to the resiatance to temperature table
+ * @tbl_size: size of the resistance to temperature table
+ * @res: resistance to calculate the temperature from
+ *
+ * This function returns the battery temperature in degrees Celcius
+ * based on the NTC resistance.
+ */
+static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
+ const struct abx500_res_to_temp *tbl, int tbl_size, int res)
+{
+ int i, temp;
+ /*
+ * Calculate the formula for the straight line
+ * Simple interpolation if we are within
+ * the resistance table limits, extrapolate
+ * if resistance is outside the limits.
+ */
+ if (res > tbl[0].resist)
+ i = 0;
+ else if (res <= tbl[tbl_size - 1].resist)
+ i = tbl_size - 2;
+ else {
+ i = 0;
+ while (!(res <= tbl[i].resist &&
+ res > tbl[i + 1].resist))
+ i++;
+ }
+
+ temp = tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
+ (res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
+ return temp;
+}
+
+/**
+ * ab8500_btemp_measure_temp() - measure battery temperature
+ * @di: pointer to the ab8500_btemp structure
+ *
+ * Returns battery temperature (on success) else the previous temperature
+ */
+static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
+{
+ int temp;
+ static int prev;
+ int rbat, rntc, vntc;
+ u8 id;
+
+ id = di->bat->batt_id;
+
+ if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL &&
+ id != BATTERY_UNKNOWN) {
+
+ rbat = ab8500_btemp_get_batctrl_res(di);
+ if (rbat < 0) {
+ dev_err(di->dev, "%s get batctrl res failed\n",
+ __func__);
+ /*
+ * Return out-of-range temperature so that
+ * charging is stopped
+ */
+ return BTEMP_THERMAL_LOW_LIMIT;
+ }
+
+ temp = ab8500_btemp_res_to_temp(di,
+ di->bat->bat_type[id].r_to_t_tbl,
+ di->bat->bat_type[id].n_temp_tbl_elements, rbat);
+ } else {
+ vntc = ab8500_gpadc_convert(di->gpadc, BTEMP_BALL);
+ if (vntc < 0) {
+ dev_err(di->dev,
+ "%s gpadc conversion failed,"
+ " using previous value\n", __func__);
+ return prev;
+ }
+ /*
+ * The PCB NTC is sourced from VTVOUT via a 230kOhm
+ * resistor.
+ */
+ rntc = 230000 * vntc / (VTVOUT_V - vntc);
+
+ temp = ab8500_btemp_res_to_temp(di,
+ di->bat->bat_type[id].r_to_t_tbl,
+ di->bat->bat_type[id].n_temp_tbl_elements, rntc);
+ prev = temp;
+ }
+ dev_dbg(di->dev, "Battery temperature is %d\n", temp);
+ return temp;
+}
+
+/**
+ * ab8500_btemp_id() - Identify the connected battery
+ * @di: pointer to the ab8500_btemp structure
+ *
+ * This function will try to identify the battery by reading the ID
+ * resistor. Some brands use a combined ID resistor with a NTC resistor to
+ * both be able to identify and to read the temperature of it.
+ */
+static int ab8500_btemp_id(struct ab8500_btemp *di)
+{
+ int res;
+ u8 i;
+
+ di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
+ di->bat->batt_id = BATTERY_UNKNOWN;
+
+ res = ab8500_btemp_get_batctrl_res(di);
+ if (res < 0) {
+ dev_err(di->dev, "%s get batctrl res failed\n", __func__);
+ return -ENXIO;
+ }
+
+ /* BATTERY_UNKNOWN is defined on position 0, skip it! */
+ for (i = BATTERY_UNKNOWN + 1; i < di->bat->n_btypes; i++) {
+ if ((res <= di->bat->bat_type[i].resis_high) &&
+ (res >= di->bat->bat_type[i].resis_low)) {
+ dev_dbg(di->dev, "Battery detected on %s"
+ " low %d < res %d < high: %d"
+ " index: %d\n",
+ di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL ?
+ "BATCTRL" : "BATTEMP",
+ di->bat->bat_type[i].resis_low, res,
+ di->bat->bat_type[i].resis_high, i);
+
+ di->bat->batt_id = i;
+ break;
+ }
+ }
+
+ if (di->bat->batt_id == BATTERY_UNKNOWN) {
+ dev_warn(di->dev, "Battery identified as unknown"
+ ", resistance %d Ohm\n", res);
+ return -ENXIO;
+ }
+
+ /*
+ * We only have to change current source if the
+ * detected type is Type 1, else we use the 7uA source
+ */
+ if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL &&
+ di->bat->batt_id == 1) {
+ dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
+ di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
+ }
+
+ return di->bat->batt_id;
+}
+
+/**
+ * ab8500_btemp_periodic_work() - Measuring the temperature periodically
+ * @work: pointer to the work_struct structure
+ *
+ * Work function for measuring the temperature periodically
+ */
+static void ab8500_btemp_periodic_work(struct work_struct *work)
+{
+ int interval;
+ struct ab8500_btemp *di = container_of(work,
+ struct ab8500_btemp, btemp_periodic_work.work);
+
+ di->bat_temp = ab8500_btemp_measure_temp(di);
+
+ if (di->bat_temp != di->prev_bat_temp) {
+ di->prev_bat_temp = di->bat_temp;
+ power_supply_changed(&di->btemp_psy);
+ }
+
+ if (di->events.ac_conn || di->events.usb_conn)
+ interval = di->bat->temp_interval_chg;
+ else
+ interval = di->bat->temp_interval_nochg;
+
+ /* Schedule a new measurement */
+ queue_delayed_work(di->btemp_wq,
+ &di->btemp_periodic_work,
+ round_jiffies(interval * HZ));
+}
+
+/**
+ * ab8500_btemp_batctrlindb_handler() - battery removal detected
+ * @irq: interrupt number
+ * @_di: void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_batctrlindb_handler(int irq, void *_di)
+{
+ struct ab8500_btemp *di = _di;
+ dev_err(di->dev, "Battery removal detected!\n");
+
+ di->events.batt_rem = true;
+ power_supply_changed(&di->btemp_psy);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_templow_handler() - battery temp lower than 10 degrees
+ * @irq: interrupt number
+ * @_di: void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)
+{
+ struct ab8500_btemp *di = _di;
+
+ if (is_ab8500_2p0_or_earlier(di->parent)) {
+ dev_dbg(di->dev, "Ignore false btemp low irq"
+ " for ABB cut 1.0, 1.1 and 2.0\n");
+ } else {
+ dev_crit(di->dev, "Battery temperature lower than -10deg c\n");
+
+ di->events.btemp_low = true;
+ di->events.btemp_high = false;
+ di->events.btemp_medhigh = false;
+ di->events.btemp_lowmed = false;
+ power_supply_changed(&di->btemp_psy);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_temphigh_handler() - battery temp higher than max temp
+ * @irq: interrupt number
+ * @_di: void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_temphigh_handler(int irq, void *_di)
+{
+ struct ab8500_btemp *di = _di;
+
+ dev_crit(di->dev, "Battery temperature is higher than MAX temp\n");
+
+ di->events.btemp_high = true;
+ di->events.btemp_medhigh = false;
+ di->events.btemp_lowmed = false;
+ di->events.btemp_low = false;
+ power_supply_changed(&di->btemp_psy);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_lowmed_handler() - battery temp between low and medium
+ * @irq: interrupt number
+ * @_di: void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_lowmed_handler(int irq, void *_di)
+{
+ struct ab8500_btemp *di = _di;
+
+ dev_dbg(di->dev, "Battery temperature is between low and medium\n");
+
+ di->events.btemp_lowmed = true;
+ di->events.btemp_medhigh = false;
+ di->events.btemp_high = false;
+ di->events.btemp_low = false;
+ power_supply_changed(&di->btemp_psy);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_medhigh_handler() - battery temp between medium and high
+ * @irq: interrupt number
+ * @_di: void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_medhigh_handler(int irq, void *_di)
+{
+ struct ab8500_btemp *di = _di;
+
+ dev_dbg(di->dev, "Battery temperature is between medium and high\n");
+
+ di->events.btemp_medhigh = true;
+ di->events.btemp_lowmed = false;
+ di->events.btemp_high = false;
+ di->events.btemp_low = false;
+ power_supply_changed(&di->btemp_psy);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_periodic() - Periodic temperature measurements
+ * @di: pointer to the ab8500_btemp structure
+ * @enable: enable or disable periodic temperature measurements
+ *
+ * Starts of stops periodic temperature measurements. Periodic measurements
+ * should only be done when a charger is connected.
+ */
+static void ab8500_btemp_periodic(struct ab8500_btemp *di,
+ bool enable)
+{
+ dev_dbg(di->dev, "Enable periodic temperature measurements: %d\n",
+ enable);
+ /*
+ * Make sure a new measurement is done directly by cancelling
+ * any pending work
+ */
+ cancel_delayed_work_sync(&di->btemp_periodic_work);
+
+ if (enable)
+ queue_delayed_work(di->btemp_wq, &di->btemp_periodic_work, 0);
+}
+
+/**
+ * ab8500_btemp_get_temp() - get battery temperature
+ * @di: pointer to the ab8500_btemp structure
+ *
+ * Returns battery temperature
+ */
+static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
+{
+ int temp = 0;
+
+ /*
+ * The BTEMP events are not reliabe on AB8500 cut2.0
+ * and prior versions
+ */
+ if (is_ab8500_2p0_or_earlier(di->parent)) {
+ temp = di->bat_temp * 10;
+ } else {
+ if (di->events.btemp_low) {
+ if (temp > di->btemp_ranges.btemp_low_limit)
+ temp = di->btemp_ranges.btemp_low_limit;
+ else
+ temp = di->bat_temp * 10;
+ } else if (di->events.btemp_high) {
+ if (temp < di->btemp_ranges.btemp_high_limit)
+ temp = di->btemp_ranges.btemp_high_limit;
+ else
+ temp = di->bat_temp * 10;
+ } else if (di->events.btemp_lowmed) {
+ if (temp > di->btemp_ranges.btemp_med_limit)
+ temp = di->btemp_ranges.btemp_med_limit;
+ else
+ temp = di->bat_temp * 10;
+ } else if (di->events.btemp_medhigh) {
+ if (temp < di->btemp_ranges.btemp_med_limit)
+ temp = di->btemp_ranges.btemp_med_limit;
+ else
+ temp = di->bat_temp * 10;
+ } else
+ temp = di->bat_temp * 10;
+ }
+ return temp;
+}
+
+/**
+ * ab8500_btemp_get_batctrl_temp() - get the temperature
+ * @btemp: pointer to the btemp structure
+ *
+ * Returns the batctrl temperature in millidegrees
+ */
+int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp)
+{
+ return btemp->bat_temp * 1000;
+}
+
+/**
+ * ab8500_btemp_get_property() - get the btemp properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the btemp
+ * properties by reading the sysfs files.
+ * online: presence of the battery
+ * present: presence of the battery
+ * technology: battery technology
+ * temp: battery temperature
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_btemp_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ab8500_btemp *di;
+
+ di = to_ab8500_btemp_device_info(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (di->events.batt_rem)
+ val->intval = 0;
+ else
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = di->bat->bat_type[di->bat->batt_id].name;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = ab8500_btemp_get_temp(di);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data)
+{
+ struct power_supply *psy;
+ struct power_supply *ext;
+ struct ab8500_btemp *di;
+ union power_supply_propval ret;
+ int i, j;
+ bool psy_found = false;
+
+ psy = (struct power_supply *)data;
+ ext = dev_get_drvdata(dev);
+ di = to_ab8500_btemp_device_info(psy);
+
+ /*
+ * For all psy where the name of your driver
+ * appears in any supplied_to
+ */
+ for (i = 0; i < ext->num_supplicants; i++) {
+ if (!strcmp(ext->supplied_to[i], psy->name))
+ psy_found = true;
+ }
+
+ if (!psy_found)
+ return 0;
+
+ /* Go through all properties for the psy */
+ for (j = 0; j < ext->num_properties; j++) {
+ enum power_supply_property prop;
+ prop = ext->properties[j];
+
+ if (ext->get_property(ext, prop, &ret))
+ continue;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_MAINS:
+ /* AC disconnected */
+ if (!ret.intval && di->events.ac_conn) {
+ di->events.ac_conn = false;
+ }
+ /* AC connected */
+ else if (ret.intval && !di->events.ac_conn) {
+ di->events.ac_conn = true;
+ if (!di->events.usb_conn)
+ ab8500_btemp_periodic(di, true);
+ }
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ /* USB disconnected */
+ if (!ret.intval && di->events.usb_conn) {
+ di->events.usb_conn = false;
+ }
+ /* USB connected */
+ else if (ret.intval && !di->events.usb_conn) {
+ di->events.usb_conn = true;
+ if (!di->events.ac_conn)
+ ab8500_btemp_periodic(di, true);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ab8500_btemp_external_power_changed() - callback for power supply changes
+ * @psy: pointer to the structure power_supply
+ *
+ * This function is pointing to the function pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in the external power
+ * supply to the btemp.
+ */
+static void ab8500_btemp_external_power_changed(struct power_supply *psy)
+{
+ struct ab8500_btemp *di = to_ab8500_btemp_device_info(psy);
+
+ class_for_each_device(power_supply_class, NULL,
+ &di->btemp_psy, ab8500_btemp_get_ext_psy_data);
+}
+
+/* ab8500 btemp driver interrupts and their respective isr */
+static struct ab8500_btemp_interrupts ab8500_btemp_irq[] = {
+ {"BAT_CTRL_INDB", ab8500_btemp_batctrlindb_handler},
+ {"BTEMP_LOW", ab8500_btemp_templow_handler},
+ {"BTEMP_HIGH", ab8500_btemp_temphigh_handler},
+ {"BTEMP_LOW_MEDIUM", ab8500_btemp_lowmed_handler},
+ {"BTEMP_MEDIUM_HIGH", ab8500_btemp_medhigh_handler},
+};
+
+#if defined(CONFIG_PM)
+static int ab8500_btemp_resume(struct platform_device *pdev)
+{
+ struct ab8500_btemp *di = platform_get_drvdata(pdev);
+
+ ab8500_btemp_periodic(di, true);
+
+ return 0;
+}
+
+static int ab8500_btemp_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct ab8500_btemp *di = platform_get_drvdata(pdev);
+
+ ab8500_btemp_periodic(di, false);
+
+ return 0;
+}
+#else
+#define ab8500_btemp_suspend NULL
+#define ab8500_btemp_resume NULL
+#endif
+
+static int __devexit ab8500_btemp_remove(struct platform_device *pdev)
+{
+ struct ab8500_btemp *di = platform_get_drvdata(pdev);
+ int i, irq;
+
+ /* Disable interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
+ free_irq(irq, di);
+ }
+
+ /* Delete the work queue */
+ destroy_workqueue(di->btemp_wq);
+
+ flush_scheduled_work();
+ power_supply_unregister(&di->btemp_psy);
+ platform_set_drvdata(pdev, NULL);
+ kfree(di);
+
+ return 0;
+}
+
+static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
+{
+ int irq, i, ret = 0;
+ u8 val;
+ struct abx500_bm_plat_data *plat_data;
+
+ struct ab8500_btemp *di =
+ kzalloc(sizeof(struct ab8500_btemp), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ /* get parent data */
+ di->dev = &pdev->dev;
+ di->parent = dev_get_drvdata(pdev->dev.parent);
+ di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+
+ /* get btemp specific platform data */
+ plat_data = pdev->dev.platform_data;
+ di->pdata = plat_data->btemp;
+ if (!di->pdata) {
+ dev_err(di->dev, "no btemp platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ /* get battery specific platform data */
+ di->bat = plat_data->battery;
+ if (!di->bat) {
+ dev_err(di->dev, "no battery platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ /* BTEMP supply */
+ di->btemp_psy.name = "ab8500_btemp";
+ di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->btemp_psy.properties = ab8500_btemp_props;
+ di->btemp_psy.num_properties = ARRAY_SIZE(ab8500_btemp_props);
+ di->btemp_psy.get_property = ab8500_btemp_get_property;
+ di->btemp_psy.supplied_to = di->pdata->supplied_to;
+ di->btemp_psy.num_supplicants = di->pdata->num_supplicants;
+ di->btemp_psy.external_power_changed =
+ ab8500_btemp_external_power_changed;
+
+
+ /* Create a work queue for the btemp */
+ di->btemp_wq =
+ create_singlethread_workqueue("ab8500_btemp_wq");
+ if (di->btemp_wq == NULL) {
+ dev_err(di->dev, "failed to create work queue\n");
+ goto free_device_info;
+ }
+
+ /* Init work for measuring temperature periodically */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->btemp_periodic_work,
+ ab8500_btemp_periodic_work);
+
+ /* Identify the battery */
+ if (ab8500_btemp_id(di) < 0)
+ dev_warn(di->dev, "failed to identify the battery\n");
+
+ /* Set BTEMP thermal limits. Low and Med are fixed */
+ di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
+ di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_BTEMP_HIGH_TH, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ goto free_btemp_wq;
+ }
+ switch (val) {
+ case BTEMP_HIGH_TH_57_0:
+ case BTEMP_HIGH_TH_57_1:
+ di->btemp_ranges.btemp_high_limit =
+ BTEMP_THERMAL_HIGH_LIMIT_57;
+ break;
+ case BTEMP_HIGH_TH_52:
+ di->btemp_ranges.btemp_high_limit =
+ BTEMP_THERMAL_HIGH_LIMIT_52;
+ break;
+ case BTEMP_HIGH_TH_62:
+ di->btemp_ranges.btemp_high_limit =
+ BTEMP_THERMAL_HIGH_LIMIT_62;
+ break;
+ }
+
+ /* Register BTEMP power supply class */
+ ret = power_supply_register(di->dev, &di->btemp_psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register BTEMP psy\n");
+ goto free_btemp_wq;
+ }
+
+ /* Register interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
+ ret = request_threaded_irq(irq, NULL, ab8500_btemp_irq[i].isr,
+ IRQF_SHARED | IRQF_NO_SUSPEND,
+ ab8500_btemp_irq[i].name, di);
+
+ if (ret) {
+ dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+ , ab8500_btemp_irq[i].name, irq, ret);
+ goto free_irq;
+ }
+ dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+ ab8500_btemp_irq[i].name, irq, ret);
+ }
+
+ platform_set_drvdata(pdev, di);
+
+ /* Kick off periodic temperature measurements */
+ ab8500_btemp_periodic(di, true);
+ list_add_tail(&di->node, &ab8500_btemp_list);
+
+ return ret;
+
+free_irq:
+ power_supply_unregister(&di->btemp_psy);
+
+ /* We also have to free all successfully registered irqs */
+ for (i = i - 1; i >= 0; i--) {
+ irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
+ free_irq(irq, di);
+ }
+free_btemp_wq:
+ destroy_workqueue(di->btemp_wq);
+free_device_info:
+ kfree(di);
+
+ return ret;
+}
+
+static struct platform_driver ab8500_btemp_driver = {
+ .probe = ab8500_btemp_probe,
+ .remove = __devexit_p(ab8500_btemp_remove),
+ .suspend = ab8500_btemp_suspend,
+ .resume = ab8500_btemp_resume,
+ .driver = {
+ .name = "ab8500-btemp",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab8500_btemp_init(void)
+{
+ return platform_driver_register(&ab8500_btemp_driver);
+}
+
+static void __exit ab8500_btemp_exit(void)
+{
+ platform_driver_unregister(&ab8500_btemp_driver);
+}
+
+subsys_initcall_sync(ab8500_btemp_init);
+module_exit(ab8500_btemp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
+MODULE_ALIAS("platform:ab8500-btemp");
+MODULE_DESCRIPTION("AB8500 battery temperature driver");
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
new file mode 100644
index 0000000..e2b4acc
--- /dev/null
+++ b/drivers/power/ab8500_charger.c
@@ -0,0 +1,2789 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Charger driver for AB8500
+ *
+ * License Terms: GNU General Public License v2
+ * Author:
+ * Johan Palsson <johan.palsson@stericsson.com>
+ * Karl Komierowski <karl.komierowski@stericsson.com>
+ * Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/mfd/abx500/ux500_chargalg.h>
+#include <linux/usb/otg.h>
+
+/* Charger constants */
+#define NO_PW_CONN 0
+#define AC_PW_CONN 1
+#define USB_PW_CONN 2
+
+#define MAIN_WDOG_ENA 0x01
+#define MAIN_WDOG_KICK 0x02
+#define MAIN_WDOG_DIS 0x00
+#define CHARG_WD_KICK 0x01
+#define MAIN_CH_ENA 0x01
+#define MAIN_CH_NO_OVERSHOOT_ENA_N 0x02
+#define USB_CH_ENA 0x01
+#define USB_CHG_NO_OVERSHOOT_ENA_N 0x02
+#define MAIN_CH_DET 0x01
+#define MAIN_CH_CV_ON 0x04
+#define USB_CH_CV_ON 0x08
+#define VBUS_DET_DBNC100 0x02
+#define VBUS_DET_DBNC1 0x01
+#define OTP_ENABLE_WD 0x01
+
+#define MAIN_CH_INPUT_CURR_SHIFT 4
+#define VBUS_IN_CURR_LIM_SHIFT 4
+
+#define LED_INDICATOR_PWM_ENA 0x01
+#define LED_INDICATOR_PWM_DIS 0x00
+#define LED_IND_CUR_5MA 0x04
+#define LED_INDICATOR_PWM_DUTY_252_256 0xBF
+
+/* HW failure constants */
+#define MAIN_CH_TH_PROT 0x02
+#define VBUS_CH_NOK 0x08
+#define USB_CH_TH_PROT 0x02
+#define VBUS_OVV_TH 0x01
+#define MAIN_CH_NOK 0x01
+#define VBUS_DET 0x80
+
+/* UsbLineStatus register bit masks */
+#define AB8500_USB_LINK_STATUS 0x78
+#define AB8500_STD_HOST_SUSP 0x18
+
+/* Watchdog timeout constant */
+#define WD_TIMER 0x30 /* 4min */
+#define WD_KICK_INTERVAL (60 * HZ)
+
+/* Lowest charger voltage is 3.39V -> 0x4E */
+#define LOW_VOLT_REG 0x4E
+
+/* UsbLineStatus register - usb types */
+enum ab8500_charger_link_status {
+ USB_STAT_NOT_CONFIGURED,
+ USB_STAT_STD_HOST_NC,
+ USB_STAT_STD_HOST_C_NS,
+ USB_STAT_STD_HOST_C_S,
+ USB_STAT_HOST_CHG_NM,
+ USB_STAT_HOST_CHG_HS,
+ USB_STAT_HOST_CHG_HS_CHIRP,
+ USB_STAT_DEDICATED_CHG,
+ USB_STAT_ACA_RID_A,
+ USB_STAT_ACA_RID_B,
+ USB_STAT_ACA_RID_C_NM,
+ USB_STAT_ACA_RID_C_HS,
+ USB_STAT_ACA_RID_C_HS_CHIRP,
+ USB_STAT_HM_IDGND,
+ USB_STAT_RESERVED,
+ USB_STAT_NOT_VALID_LINK,
+};
+
+enum ab8500_usb_state {
+ AB8500_BM_USB_STATE_RESET_HS, /* HighSpeed Reset */
+ AB8500_BM_USB_STATE_RESET_FS, /* FullSpeed/LowSpeed Reset */
+ AB8500_BM_USB_STATE_CONFIGURED,
+ AB8500_BM_USB_STATE_SUSPEND,
+ AB8500_BM_USB_STATE_RESUME,
+ AB8500_BM_USB_STATE_MAX,
+};
+
+/* VBUS input current limits supported in AB8500 in mA */
+#define USB_CH_IP_CUR_LVL_0P05 50
+#define USB_CH_IP_CUR_LVL_0P09 98
+#define USB_CH_IP_CUR_LVL_0P19 193
+#define USB_CH_IP_CUR_LVL_0P29 290
+#define USB_CH_IP_CUR_LVL_0P38 380
+#define USB_CH_IP_CUR_LVL_0P45 450
+#define USB_CH_IP_CUR_LVL_0P5 500
+#define USB_CH_IP_CUR_LVL_0P6 600
+#define USB_CH_IP_CUR_LVL_0P7 700
+#define USB_CH_IP_CUR_LVL_0P8 800
+#define USB_CH_IP_CUR_LVL_0P9 900
+#define USB_CH_IP_CUR_LVL_1P0 1000
+#define USB_CH_IP_CUR_LVL_1P1 1100
+#define USB_CH_IP_CUR_LVL_1P3 1300
+#define USB_CH_IP_CUR_LVL_1P4 1400
+#define USB_CH_IP_CUR_LVL_1P5 1500
+
+#define VBAT_TRESH_IP_CUR_RED 3800
+
+#define to_ab8500_charger_usb_device_info(x) container_of((x), \
+ struct ab8500_charger, usb_chg)
+#define to_ab8500_charger_ac_device_info(x) container_of((x), \
+ struct ab8500_charger, ac_chg)
+
+/**
+ * struct ab8500_charger_interrupts - ab8500 interupts
+ * @name: name of the interrupt
+ * @isr function pointer to the isr
+ */
+struct ab8500_charger_interrupts {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct ab8500_charger_info {
+ int charger_connected;
+ int charger_online;
+ int charger_voltage;
+ int cv_active;
+ bool wd_expired;
+};
+
+struct ab8500_charger_event_flags {
+ bool mainextchnotok;
+ bool main_thermal_prot;
+ bool usb_thermal_prot;
+ bool vbus_ovv;
+ bool usbchargernotok;
+ bool chgwdexp;
+ bool vbus_collapse;
+};
+
+struct ab8500_charger_usb_state {
+ bool usb_changed;
+ int usb_current;
+ enum ab8500_usb_state state;
+ spinlock_t usb_lock;
+};
+
+/**
+ * struct ab8500_charger - ab8500 Charger device information
+ * @dev: Pointer to the structure device
+ * @max_usb_in_curr: Max USB charger input current
+ * @vbus_detected: VBUS detected
+ * @vbus_detected_start:
+ * VBUS detected during startup
+ * @ac_conn: This will be true when the AC charger has been plugged
+ * @vddadc_en_ac: Indicate if VDD ADC supply is enabled because AC
+ * charger is enabled
+ * @vddadc_en_usb: Indicate if VDD ADC supply is enabled because USB
+ * charger is enabled
+ * @vbat Battery voltage
+ * @old_vbat Previously measured battery voltage
+ * @autopower Indicate if we should have automatic pwron after pwrloss
+ * @parent: Pointer to the struct ab8500
+ * @gpadc: Pointer to the struct gpadc
+ * @pdata: Pointer to the abx500_charger platform data
+ * @bat: Pointer to the abx500_bm platform data
+ * @flags: Structure for information about events triggered
+ * @usb_state: Structure for usb stack information
+ * @ac_chg: AC charger power supply
+ * @usb_chg: USB charger power supply
+ * @ac: Structure that holds the AC charger properties
+ * @usb: Structure that holds the USB charger properties
+ * @regu: Pointer to the struct regulator
+ * @charger_wq: Work queue for the IRQs and checking HW state
+ * @check_vbat_work Work for checking vbat threshold to adjust vbus current
+ * @check_hw_failure_work: Work for checking HW state
+ * @check_usbchgnotok_work: Work for checking USB charger not ok status
+ * @kick_wd_work: Work for kicking the charger watchdog in case
+ * of ABB rev 1.* due to the watchog logic bug
+ * @ac_work: Work for checking AC charger connection
+ * @detect_usb_type_work: Work for detecting the USB type connected
+ * @usb_link_status_work: Work for checking the new USB link status
+ * @usb_state_changed_work: Work for checking USB state
+ * @check_main_thermal_prot_work:
+ * Work for checking Main thermal status
+ * @check_usb_thermal_prot_work:
+ * Work for checking USB thermal status
+ */
+struct ab8500_charger {
+ struct device *dev;
+ int max_usb_in_curr;
+ bool vbus_detected;
+ bool vbus_detected_start;
+ bool ac_conn;
+ bool vddadc_en_ac;
+ bool vddadc_en_usb;
+ int vbat;
+ int old_vbat;
+ bool autopower;
+ struct ab8500 *parent;
+ struct ab8500_gpadc *gpadc;
+ struct abx500_charger_platform_data *pdata;
+ struct abx500_bm_data *bat;
+ struct ab8500_charger_event_flags flags;
+ struct ab8500_charger_usb_state usb_state;
+ struct ux500_charger ac_chg;
+ struct ux500_charger usb_chg;
+ struct ab8500_charger_info ac;
+ struct ab8500_charger_info usb;
+ struct regulator *regu;
+ struct workqueue_struct *charger_wq;
+ struct delayed_work check_vbat_work;
+ struct delayed_work check_hw_failure_work;
+ struct delayed_work check_usbchgnotok_work;
+ struct delayed_work kick_wd_work;
+ struct work_struct ac_work;
+ struct work_struct detect_usb_type_work;
+ struct work_struct usb_link_status_work;
+ struct work_struct usb_state_changed_work;
+ struct work_struct check_main_thermal_prot_work;
+ struct work_struct check_usb_thermal_prot_work;
+ struct usb_phy *usb_phy;
+ struct notifier_block nb;
+};
+
+/* AC properties */
+static enum power_supply_property ab8500_charger_ac_props[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+/* USB properties */
+static enum power_supply_property ab8500_charger_usb_props[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+/**
+ * ab8500_power_loss_handling - set how we handle powerloss.
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Magic nummbers are from STE HW department.
+ */
+static void ab8500_power_loss_handling(struct ab8500_charger *di)
+{
+ u8 reg;
+ int ret;
+
+ dev_dbg(di->dev, "Autopower : %d\n", di->autopower);
+
+ /* read the autopower register */
+ ret = abx500_get_register_interruptible(di->dev, 0x15, 0x00, &reg);
+ if (ret) {
+ dev_err(di->dev, "%d write failed\n", __LINE__);
+ return;
+ }
+
+ /* enable the OPT emulation registers */
+ ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x2);
+ if (ret) {
+ dev_err(di->dev, "%d write failed\n", __LINE__);
+ return;
+ }
+
+ if (di->autopower)
+ reg |= 0x8;
+ else
+ reg &= ~0x8;
+
+ /* write back the changed value to autopower reg */
+ ret = abx500_set_register_interruptible(di->dev, 0x15, 0x00, reg);
+ if (ret) {
+ dev_err(di->dev, "%d write failed\n", __LINE__);
+ return;
+ }
+
+ /* disable the set OTP registers again */
+ ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x0);
+ if (ret) {
+ dev_err(di->dev, "%d write failed\n", __LINE__);
+ return;
+ }
+}
+
+/**
+ * ab8500_power_supply_changed - a wrapper with local extentions for
+ * power_supply_changed
+ * @di: pointer to the ab8500_charger structure
+ * @psy: pointer to power_supply_that have changed.
+ *
+ */
+static void ab8500_power_supply_changed(struct ab8500_charger *di,
+ struct power_supply *psy)
+{
+ if (di->pdata->autopower_cfg) {
+ if (!di->usb.charger_connected &&
+ !di->ac.charger_connected &&
+ di->autopower) {
+ di->autopower = false;
+ ab8500_power_loss_handling(di);
+ } else if (!di->autopower &&
+ (di->ac.charger_connected ||
+ di->usb.charger_connected)) {
+ di->autopower = true;
+ ab8500_power_loss_handling(di);
+ }
+ }
+ power_supply_changed(psy);
+}
+
+static void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
+ bool connected)
+{
+ if (connected != di->usb.charger_connected) {
+ dev_dbg(di->dev, "USB connected:%i\n", connected);
+ di->usb.charger_connected = connected;
+ sysfs_notify(&di->usb_chg.psy.dev->kobj, NULL, "present");
+ }
+}
+
+/**
+ * ab8500_charger_get_ac_voltage() - get ac charger voltage
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Returns ac charger voltage (on success)
+ */
+static int ab8500_charger_get_ac_voltage(struct ab8500_charger *di)
+{
+ int vch;
+
+ /* Only measure voltage if the charger is connected */
+ if (di->ac.charger_connected) {
+ vch = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_V);
+ if (vch < 0)
+ dev_err(di->dev, "%s gpadc conv failed,\n", __func__);
+ } else {
+ vch = 0;
+ }
+ return vch;
+}
+
+/**
+ * ab8500_charger_ac_cv() - check if the main charger is in CV mode
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Returns ac charger CV mode (on success) else error code
+ */
+static int ab8500_charger_ac_cv(struct ab8500_charger *di)
+{
+ u8 val;
+ int ret = 0;
+
+ /* Only check CV mode if the charger is online */
+ if (di->ac.charger_online) {
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_STATUS1_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return 0;
+ }
+
+ if (val & MAIN_CH_CV_ON)
+ ret = 1;
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_get_vbus_voltage() - get vbus voltage
+ * @di: pointer to the ab8500_charger structure
+ *
+ * This function returns the vbus voltage.
+ * Returns vbus voltage (on success)
+ */
+static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di)
+{
+ int vch;
+
+ /* Only measure voltage if the charger is connected */
+ if (di->usb.charger_connected) {
+ vch = ab8500_gpadc_convert(di->gpadc, VBUS_V);
+ if (vch < 0)
+ dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+ } else {
+ vch = 0;
+ }
+ return vch;
+}
+
+/**
+ * ab8500_charger_get_usb_current() - get usb charger current
+ * @di: pointer to the ab8500_charger structure
+ *
+ * This function returns the usb charger current.
+ * Returns usb current (on success) and error code on failure
+ */
+static int ab8500_charger_get_usb_current(struct ab8500_charger *di)
+{
+ int ich;
+
+ /* Only measure current if the charger is online */
+ if (di->usb.charger_online) {
+ ich = ab8500_gpadc_convert(di->gpadc, USB_CHARGER_C);
+ if (ich < 0)
+ dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+ } else {
+ ich = 0;
+ }
+ return ich;
+}
+
+/**
+ * ab8500_charger_get_ac_current() - get ac charger current
+ * @di: pointer to the ab8500_charger structure
+ *
+ * This function returns the ac charger current.
+ * Returns ac current (on success) and error code on failure.
+ */
+static int ab8500_charger_get_ac_current(struct ab8500_charger *di)
+{
+ int ich;
+
+ /* Only measure current if the charger is online */
+ if (di->ac.charger_online) {
+ ich = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_C);
+ if (ich < 0)
+ dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+ } else {
+ ich = 0;
+ }
+ return ich;
+}
+
+/**
+ * ab8500_charger_usb_cv() - check if the usb charger is in CV mode
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Returns ac charger CV mode (on success) else error code
+ */
+static int ab8500_charger_usb_cv(struct ab8500_charger *di)
+{
+ int ret;
+ u8 val;
+
+ /* Only check CV mode if the charger is online */
+ if (di->usb.charger_online) {
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_USBCH_STAT1_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return 0;
+ }
+
+ if (val & USB_CH_CV_ON)
+ ret = 1;
+ else
+ ret = 0;
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_detect_chargers() - Detect the connected chargers
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Returns the type of charger connected.
+ * For USB it will not mean we can actually charge from it
+ * but that there is a USB cable connected that we have to
+ * identify. This is used during startup when we don't get
+ * interrupts of the charger detection
+ *
+ * Returns an integer value, that means,
+ * NO_PW_CONN no power supply is connected
+ * AC_PW_CONN if the AC power supply is connected
+ * USB_PW_CONN if the USB power supply is connected
+ * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
+ */
+static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
+{
+ int result = NO_PW_CONN;
+ int ret;
+ u8 val;
+
+ /* Check for AC charger */
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_STATUS1_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return ret;
+ }
+
+ if (val & MAIN_CH_DET)
+ result = AC_PW_CONN;
+
+ /* Check for USB charger */
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_USBCH_STAT1_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return ret;
+ }
+
+ if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC100))
+ result |= USB_PW_CONN;
+
+ return result;
+}
+
+/**
+ * ab8500_charger_max_usb_curr() - get the max curr for the USB type
+ * @di: pointer to the ab8500_charger structure
+ * @link_status: the identified USB type
+ *
+ * Get the maximum current that is allowed to be drawn from the host
+ * based on the USB type.
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
+ enum ab8500_charger_link_status link_status)
+{
+ int ret = 0;
+
+ switch (link_status) {
+ case USB_STAT_STD_HOST_NC:
+ case USB_STAT_STD_HOST_C_NS:
+ case USB_STAT_STD_HOST_C_S:
+ dev_dbg(di->dev, "USB Type - Standard host is "
+ "detected through USB driver\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
+ break;
+ case USB_STAT_HOST_CHG_HS_CHIRP:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ break;
+ case USB_STAT_HOST_CHG_HS:
+ case USB_STAT_ACA_RID_C_HS:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P9;
+ break;
+ case USB_STAT_ACA_RID_A:
+ /*
+ * Dedicated charger level minus maximum current accessory
+ * can consume (300mA). Closest level is 1100mA
+ */
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P1;
+ break;
+ case USB_STAT_ACA_RID_B:
+ /*
+ * Dedicated charger level minus 120mA (20mA for ACA and
+ * 100mA for potential accessory). Closest level is 1300mA
+ */
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
+ break;
+ case USB_STAT_DEDICATED_CHG:
+ case USB_STAT_HOST_CHG_NM:
+ case USB_STAT_ACA_RID_C_HS_CHIRP:
+ case USB_STAT_ACA_RID_C_NM:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
+ break;
+ case USB_STAT_RESERVED:
+ /*
+ * This state is used to indicate that VBUS has dropped below
+ * the detection level 4 times in a row. This is due to the
+ * charger output current is set to high making the charger
+ * voltage collapse. This have to be propagated through to
+ * chargalg. This is done using the property
+ * POWER_SUPPLY_PROP_CURRENT_AVG = 1
+ */
+ di->flags.vbus_collapse = true;
+ dev_dbg(di->dev, "USB Type - USB_STAT_RESERVED "
+ "VBUS has collapsed\n");
+ ret = -1;
+ break;
+ case USB_STAT_HM_IDGND:
+ case USB_STAT_NOT_CONFIGURED:
+ case USB_STAT_NOT_VALID_LINK:
+ dev_err(di->dev, "USB Type - Charging not allowed\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+ ret = -ENXIO;
+ break;
+ default:
+ dev_err(di->dev, "USB Type - Unknown\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+ ret = -ENXIO;
+ break;
+ };
+
+ dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
+ link_status, di->max_usb_in_curr);
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_read_usb_type() - read the type of usb connected
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Detect the type of the plugged USB
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_charger_read_usb_type(struct ab8500_charger *di)
+{
+ int ret;
+ u8 val;
+
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return ret;
+ }
+ ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+ AB8500_USB_LINE_STAT_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return ret;
+ }
+
+ /* get the USB type */
+ val = (val & AB8500_USB_LINK_STATUS) >> 3;
+ ret = ab8500_charger_max_usb_curr(di,
+ (enum ab8500_charger_link_status) val);
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_detect_usb_type() - get the type of usb connected
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Detect the type of the plugged USB
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
+{
+ int i, ret;
+ u8 val;
+
+ /*
+ * On getting the VBUS rising edge detect interrupt there
+ * is a 250ms delay after which the register UsbLineStatus
+ * is filled with valid data.
+ */
+ for (i = 0; i < 10; i++) {
+ msleep(250);
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG,
+ &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return ret;
+ }
+ ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+ AB8500_USB_LINE_STAT_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return ret;
+ }
+ /*
+ * Until the IT source register is read the UsbLineStatus
+ * register is not updated, hence doing the same
+ * Revisit this:
+ */
+
+ /* get the USB type */
+ val = (val & AB8500_USB_LINK_STATUS) >> 3;
+ if (val)
+ break;
+ }
+ ret = ab8500_charger_max_usb_curr(di,
+ (enum ab8500_charger_link_status) val);
+
+ return ret;
+}
+
+/*
+ * This array maps the raw hex value to charger voltage used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_charger_voltage_map[] = {
+ 3500 ,
+ 3525 ,
+ 3550 ,
+ 3575 ,
+ 3600 ,
+ 3625 ,
+ 3650 ,
+ 3675 ,
+ 3700 ,
+ 3725 ,
+ 3750 ,
+ 3775 ,
+ 3800 ,
+ 3825 ,
+ 3850 ,
+ 3875 ,
+ 3900 ,
+ 3925 ,
+ 3950 ,
+ 3975 ,
+ 4000 ,
+ 4025 ,
+ 4050 ,
+ 4060 ,
+ 4070 ,
+ 4080 ,
+ 4090 ,
+ 4100 ,
+ 4110 ,
+ 4120 ,
+ 4130 ,
+ 4140 ,
+ 4150 ,
+ 4160 ,
+ 4170 ,
+ 4180 ,
+ 4190 ,
+ 4200 ,
+ 4210 ,
+ 4220 ,
+ 4230 ,
+ 4240 ,
+ 4250 ,
+ 4260 ,
+ 4270 ,
+ 4280 ,
+ 4290 ,
+ 4300 ,
+ 4310 ,
+ 4320 ,
+ 4330 ,
+ 4340 ,
+ 4350 ,
+ 4360 ,
+ 4370 ,
+ 4380 ,
+ 4390 ,
+ 4400 ,
+ 4410 ,
+ 4420 ,
+ 4430 ,
+ 4440 ,
+ 4450 ,
+ 4460 ,
+ 4470 ,
+ 4480 ,
+ 4490 ,
+ 4500 ,
+ 4510 ,
+ 4520 ,
+ 4530 ,
+ 4540 ,
+ 4550 ,
+ 4560 ,
+ 4570 ,
+ 4580 ,
+ 4590 ,
+ 4600 ,
+};
+
+/*
+ * This array maps the raw hex value to charger current used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_charger_current_map[] = {
+ 100 ,
+ 200 ,
+ 300 ,
+ 400 ,
+ 500 ,
+ 600 ,
+ 700 ,
+ 800 ,
+ 900 ,
+ 1000 ,
+ 1100 ,
+ 1200 ,
+ 1300 ,
+ 1400 ,
+ 1500 ,
+};
+
+/*
+ * This array maps the raw hex value to VBUS input current used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_charger_vbus_in_curr_map[] = {
+ USB_CH_IP_CUR_LVL_0P05,
+ USB_CH_IP_CUR_LVL_0P09,
+ USB_CH_IP_CUR_LVL_0P19,
+ USB_CH_IP_CUR_LVL_0P29,
+ USB_CH_IP_CUR_LVL_0P38,
+ USB_CH_IP_CUR_LVL_0P45,
+ USB_CH_IP_CUR_LVL_0P5,
+ USB_CH_IP_CUR_LVL_0P6,
+ USB_CH_IP_CUR_LVL_0P7,
+ USB_CH_IP_CUR_LVL_0P8,
+ USB_CH_IP_CUR_LVL_0P9,
+ USB_CH_IP_CUR_LVL_1P0,
+ USB_CH_IP_CUR_LVL_1P1,
+ USB_CH_IP_CUR_LVL_1P3,
+ USB_CH_IP_CUR_LVL_1P4,
+ USB_CH_IP_CUR_LVL_1P5,
+};
+
+static int ab8500_voltage_to_regval(int voltage)
+{
+ int i;
+
+ /* Special case for voltage below 3.5V */
+ if (voltage < ab8500_charger_voltage_map[0])
+ return LOW_VOLT_REG;
+
+ for (i = 1; i < ARRAY_SIZE(ab8500_charger_voltage_map); i++) {
+ if (voltage < ab8500_charger_voltage_map[i])
+ return i - 1;
+ }
+
+ /* If not last element, return error */
+ i = ARRAY_SIZE(ab8500_charger_voltage_map) - 1;
+ if (voltage == ab8500_charger_voltage_map[i])
+ return i;
+ else
+ return -1;
+}
+
+static int ab8500_current_to_regval(int curr)
+{
+ int i;
+
+ if (curr < ab8500_charger_current_map[0])
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_charger_current_map); i++) {
+ if (curr < ab8500_charger_current_map[i])
+ return i - 1;
+ }
+
+ /* If not last element, return error */
+ i = ARRAY_SIZE(ab8500_charger_current_map) - 1;
+ if (curr == ab8500_charger_current_map[i])
+ return i;
+ else
+ return -1;
+}
+
+static int ab8500_vbus_in_curr_to_regval(int curr)
+{
+ int i;
+
+ if (curr < ab8500_charger_vbus_in_curr_map[0])
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_charger_vbus_in_curr_map); i++) {
+ if (curr < ab8500_charger_vbus_in_curr_map[i])
+ return i - 1;
+ }
+
+ /* If not last element, return error */
+ i = ARRAY_SIZE(ab8500_charger_vbus_in_curr_map) - 1;
+ if (curr == ab8500_charger_vbus_in_curr_map[i])
+ return i;
+ else
+ return -1;
+}
+
+/**
+ * ab8500_charger_get_usb_cur() - get usb current
+ * @di: pointer to the ab8500_charger structre
+ *
+ * The usb stack provides the maximum current that can be drawn from
+ * the standard usb host. This will be in mA.
+ * This function converts current in mA to a value that can be written
+ * to the register. Returns -1 if charging is not allowed
+ */
+static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
+{
+ switch (di->usb_state.usb_current) {
+ case 100:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
+ break;
+ case 200:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P19;
+ break;
+ case 300:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P29;
+ break;
+ case 400:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P38;
+ break;
+ case 500:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ break;
+ default:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+ return -1;
+ break;
+ };
+ return 0;
+}
+
+/**
+ * ab8500_charger_set_vbus_in_curr() - set VBUS input current limit
+ * @di: pointer to the ab8500_charger structure
+ * @ich_in: charger input current limit
+ *
+ * Sets the current that can be drawn from the USB host
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
+ int ich_in)
+{
+ int ret;
+ int input_curr_index;
+ int min_value;
+
+ /* We should always use to lowest current limit */
+ min_value = min(di->bat->chg_params->usb_curr_max, ich_in);
+
+ switch (min_value) {
+ case 100:
+ if (di->vbat < VBAT_TRESH_IP_CUR_RED)
+ min_value = USB_CH_IP_CUR_LVL_0P05;
+ break;
+ case 500:
+ if (di->vbat < VBAT_TRESH_IP_CUR_RED)
+ min_value = USB_CH_IP_CUR_LVL_0P45;
+ break;
+ default:
+ break;
+ }
+
+ input_curr_index = ab8500_vbus_in_curr_to_regval(min_value);
+ if (input_curr_index < 0) {
+ dev_err(di->dev, "VBUS input current limit too high\n");
+ return -ENXIO;
+ }
+
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_USBCH_IPT_CRNTLVL_REG,
+ input_curr_index << VBUS_IN_CURR_LIM_SHIFT);
+ if (ret)
+ dev_err(di->dev, "%s write failed\n", __func__);
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_led_en() - turn on/off chargign led
+ * @di: pointer to the ab8500_charger structure
+ * @on: flag to turn on/off the chargign led
+ *
+ * Power ON/OFF charging LED indication
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_led_en(struct ab8500_charger *di, int on)
+{
+ int ret;
+
+ if (on) {
+ /* Power ON charging LED indicator, set LED current to 5mA */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_LED_INDICATOR_PWM_CTRL,
+ (LED_IND_CUR_5MA | LED_INDICATOR_PWM_ENA));
+ if (ret) {
+ dev_err(di->dev, "Power ON LED failed\n");
+ return ret;
+ }
+ /* LED indicator PWM duty cycle 252/256 */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_LED_INDICATOR_PWM_DUTY,
+ LED_INDICATOR_PWM_DUTY_252_256);
+ if (ret) {
+ dev_err(di->dev, "Set LED PWM duty cycle failed\n");
+ return ret;
+ }
+ } else {
+ /* Power off charging LED indicator */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_LED_INDICATOR_PWM_CTRL,
+ LED_INDICATOR_PWM_DIS);
+ if (ret) {
+ dev_err(di->dev, "Power-off LED failed\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_ac_en() - enable or disable ac charging
+ * @di: pointer to the ab8500_charger structure
+ * @enable: enable/disable flag
+ * @vset: charging voltage
+ * @iset: charging current
+ *
+ * Enable/Disable AC/Mains charging and turns on/off the charging led
+ * respectively.
+ **/
+static int ab8500_charger_ac_en(struct ux500_charger *charger,
+ int enable, int vset, int iset)
+{
+ int ret;
+ int volt_index;
+ int curr_index;
+ int input_curr_index;
+ u8 overshoot = 0;
+
+ struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger);
+
+ if (enable) {
+ /* Check if AC is connected */
+ if (!di->ac.charger_connected) {
+ dev_err(di->dev, "AC charger not connected\n");
+ return -ENXIO;
+ }
+
+ /* Enable AC charging */
+ dev_dbg(di->dev, "Enable AC: %dmV %dmA\n", vset, iset);
+
+ /*
+ * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
+ * will be triggered everytime we enable the VDD ADC supply.
+ * This will turn off charging for a short while.
+ * It can be avoided by having the supply on when
+ * there is a charger enabled. Normally the VDD ADC supply
+ * is enabled everytime a GPADC conversion is triggered. We will
+ * force it to be enabled from this driver to have
+ * the GPADC module independant of the AB8500 chargers
+ */
+ if (!di->vddadc_en_ac) {
+ regulator_enable(di->regu);
+ di->vddadc_en_ac = true;
+ }
+
+ /* Check if the requested voltage or current is valid */
+ volt_index = ab8500_voltage_to_regval(vset);
+ curr_index = ab8500_current_to_regval(iset);
+ input_curr_index = ab8500_current_to_regval(
+ di->bat->chg_params->ac_curr_max);
+ if (volt_index < 0 || curr_index < 0 || input_curr_index < 0) {
+ dev_err(di->dev,
+ "Charger voltage or current too high, "
+ "charging not started\n");
+ return -ENXIO;
+ }
+
+ /* ChVoltLevel: maximum battery charging voltage */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+ /* MainChInputCurr: current that can be drawn from the charger*/
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_MCH_IPT_CURLVL_REG,
+ input_curr_index << MAIN_CH_INPUT_CURR_SHIFT);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+ /* ChOutputCurentLevel: protected output current */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ /* Check if VBAT overshoot control should be enabled */
+ if (!di->bat->enable_overshoot)
+ overshoot = MAIN_CH_NO_OVERSHOOT_ENA_N;
+
+ /* Enable Main Charger */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_MCH_CTRL1, MAIN_CH_ENA | overshoot);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ /* Power on charging LED indication */
+ ret = ab8500_charger_led_en(di, true);
+ if (ret < 0)
+ dev_err(di->dev, "failed to enable LED\n");
+
+ di->ac.charger_online = 1;
+ } else {
+ /* Disable AC charging */
+ if (is_ab8500_1p1_or_earlier(di->parent)) {
+ /*
+ * For ABB revision 1.0 and 1.1 there is a bug in the
+ * watchdog logic. That means we have to continously
+ * kick the charger watchdog even when no charger is
+ * connected. This is only valid once the AC charger
+ * has been enabled. This is a bug that is not handled
+ * by the algorithm and the watchdog have to be kicked
+ * by the charger driver when the AC charger
+ * is disabled
+ */
+ if (di->ac_conn) {
+ queue_delayed_work(di->charger_wq,
+ &di->kick_wd_work,
+ round_jiffies(WD_KICK_INTERVAL));
+ }
+
+ /*
+ * We can't turn off charging completely
+ * due to a bug in AB8500 cut1.
+ * If we do, charging will not start again.
+ * That is why we set the lowest voltage
+ * and current possible
+ */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_CH_VOLT_LVL_REG, CH_VOL_LVL_3P5);
+ if (ret) {
+ dev_err(di->dev,
+ "%s write failed\n", __func__);
+ return ret;
+ }
+
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_CH_OPT_CRNTLVL_REG, CH_OP_CUR_LVL_0P1);
+ if (ret) {
+ dev_err(di->dev,
+ "%s write failed\n", __func__);
+ return ret;
+ }
+ } else {
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_MCH_CTRL1, 0);
+ if (ret) {
+ dev_err(di->dev,
+ "%s write failed\n", __func__);
+ return ret;
+ }
+ }
+
+ ret = ab8500_charger_led_en(di, false);
+ if (ret < 0)
+ dev_err(di->dev, "failed to disable LED\n");
+
+ di->ac.charger_online = 0;
+ di->ac.wd_expired = false;
+
+ /* Disable regulator if enabled */
+ if (di->vddadc_en_ac) {
+ regulator_disable(di->regu);
+ di->vddadc_en_ac = false;
+ }
+
+ dev_dbg(di->dev, "%s Disabled AC charging\n", __func__);
+ }
+ ab8500_power_supply_changed(di, &di->ac_chg.psy);
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_usb_en() - enable usb charging
+ * @di: pointer to the ab8500_charger structure
+ * @enable: enable/disable flag
+ * @vset: charging voltage
+ * @ich_out: charger output current
+ *
+ * Enable/Disable USB charging and turns on/off the charging led respectively.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_usb_en(struct ux500_charger *charger,
+ int enable, int vset, int ich_out)
+{
+ int ret;
+ int volt_index;
+ int curr_index;
+ u8 overshoot = 0;
+
+ struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
+
+ if (enable) {
+ /* Check if USB is connected */
+ if (!di->usb.charger_connected) {
+ dev_err(di->dev, "USB charger not connected\n");
+ return -ENXIO;
+ }
+
+ /*
+ * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
+ * will be triggered everytime we enable the VDD ADC supply.
+ * This will turn off charging for a short while.
+ * It can be avoided by having the supply on when
+ * there is a charger enabled. Normally the VDD ADC supply
+ * is enabled everytime a GPADC conversion is triggered. We will
+ * force it to be enabled from this driver to have
+ * the GPADC module independant of the AB8500 chargers
+ */
+ if (!di->vddadc_en_usb) {
+ regulator_enable(di->regu);
+ di->vddadc_en_usb = true;
+ }
+
+ /* Enable USB charging */
+ dev_dbg(di->dev, "Enable USB: %dmV %dmA\n", vset, ich_out);
+
+ /* Check if the requested voltage or current is valid */
+ volt_index = ab8500_voltage_to_regval(vset);
+ curr_index = ab8500_current_to_regval(ich_out);
+ if (volt_index < 0 || curr_index < 0) {
+ dev_err(di->dev,
+ "Charger voltage or current too high, "
+ "charging not started\n");
+ return -ENXIO;
+ }
+
+ /* ChVoltLevel: max voltage upto which battery can be charged */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+ /* USBChInputCurr: current that can be drawn from the usb */
+ ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+ if (ret) {
+ dev_err(di->dev, "setting USBChInputCurr failed\n");
+ return ret;
+ }
+ /* ChOutputCurentLevel: protected output current */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+ /* Check if VBAT overshoot control should be enabled */
+ if (!di->bat->enable_overshoot)
+ overshoot = USB_CHG_NO_OVERSHOOT_ENA_N;
+
+ /* Enable USB Charger */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_USBCH_CTRL1_REG, USB_CH_ENA | overshoot);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ /* If success power on charging LED indication */
+ ret = ab8500_charger_led_en(di, true);
+ if (ret < 0)
+ dev_err(di->dev, "failed to enable LED\n");
+
+ queue_delayed_work(di->charger_wq, &di->check_vbat_work, HZ);
+
+ di->usb.charger_online = 1;
+ } else {
+ /* Disable USB charging */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_USBCH_CTRL1_REG, 0);
+ if (ret) {
+ dev_err(di->dev,
+ "%s write failed\n", __func__);
+ return ret;
+ }
+
+ ret = ab8500_charger_led_en(di, false);
+ if (ret < 0)
+ dev_err(di->dev, "failed to disable LED\n");
+
+ di->usb.charger_online = 0;
+ di->usb.wd_expired = false;
+
+ /* Disable regulator if enabled */
+ if (di->vddadc_en_usb) {
+ regulator_disable(di->regu);
+ di->vddadc_en_usb = false;
+ }
+
+ dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
+
+ /* Cancel any pending Vbat check work */
+ if (delayed_work_pending(&di->check_vbat_work))
+ cancel_delayed_work(&di->check_vbat_work);
+
+ }
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_watchdog_kick() - kick charger watchdog
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Kick charger watchdog
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_watchdog_kick(struct ux500_charger *charger)
+{
+ int ret;
+ struct ab8500_charger *di;
+
+ if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
+ di = to_ab8500_charger_ac_device_info(charger);
+ else if (charger->psy.type == POWER_SUPPLY_TYPE_USB)
+ di = to_ab8500_charger_usb_device_info(charger);
+ else
+ return -ENXIO;
+
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
+ if (ret)
+ dev_err(di->dev, "Failed to kick WD!\n");
+
+ return ret;
+}
+
+/**
+ * ab8500_charger_update_charger_current() - update charger current
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Update the charger output current for the specified charger
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
+ int ich_out)
+{
+ int ret;
+ int curr_index;
+ struct ab8500_charger *di;
+
+ if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
+ di = to_ab8500_charger_ac_device_info(charger);
+ else if (charger->psy.type == POWER_SUPPLY_TYPE_USB)
+ di = to_ab8500_charger_usb_device_info(charger);
+ else
+ return -ENXIO;
+
+ curr_index = ab8500_current_to_regval(ich_out);
+ if (curr_index < 0) {
+ dev_err(di->dev,
+ "Charger current too high, "
+ "charging not started\n");
+ return -ENXIO;
+ }
+
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ /* Reset the main and usb drop input current measurement counter */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CHARGER_CTRL,
+ 0x1);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int ab8500_charger_get_ext_psy_data(struct device *dev, void *data)
+{
+ struct power_supply *psy;
+ struct power_supply *ext;
+ struct ab8500_charger *di;
+ union power_supply_propval ret;
+ int i, j;
+ bool psy_found = false;
+ struct ux500_charger *usb_chg;
+
+ usb_chg = (struct ux500_charger *)data;
+ psy = &usb_chg->psy;
+
+ di = to_ab8500_charger_usb_device_info(usb_chg);
+
+ ext = dev_get_drvdata(dev);
+
+ /* For all psy where the driver name appears in any supplied_to */
+ for (i = 0; i < ext->num_supplicants; i++) {
+ if (!strcmp(ext->supplied_to[i], psy->name))
+ psy_found = true;
+ }
+
+ if (!psy_found)
+ return 0;
+
+ /* Go through all properties for the psy */
+ for (j = 0; j < ext->num_properties; j++) {
+ enum power_supply_property prop;
+ prop = ext->properties[j];
+
+ if (ext->get_property(ext, prop, &ret))
+ continue;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ di->vbat = ret.intval / 1000;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ab8500_charger_check_vbat_work() - keep vbus current within spec
+ * @work pointer to the work_struct structure
+ *
+ * Due to a asic bug it is necessary to lower the input current to the vbus
+ * charger when charging with at some specific levels. This issue is only valid
+ * for below a certain battery voltage. This function makes sure that the
+ * the allowed current limit isn't exceeded.
+ */
+static void ab8500_charger_check_vbat_work(struct work_struct *work)
+{
+ int t = 10;
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, check_vbat_work.work);
+
+ class_for_each_device(power_supply_class, NULL,
+ &di->usb_chg.psy, ab8500_charger_get_ext_psy_data);
+
+ /* First run old_vbat is 0. */
+ if (di->old_vbat == 0)
+ di->old_vbat = di->vbat;
+
+ if (!((di->old_vbat <= VBAT_TRESH_IP_CUR_RED &&
+ di->vbat <= VBAT_TRESH_IP_CUR_RED) ||
+ (di->old_vbat > VBAT_TRESH_IP_CUR_RED &&
+ di->vbat > VBAT_TRESH_IP_CUR_RED))) {
+
+ dev_dbg(di->dev, "Vbat did cross threshold, curr: %d, new: %d,"
+ " old: %d\n", di->max_usb_in_curr, di->vbat,
+ di->old_vbat);
+ ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+ power_supply_changed(&di->usb_chg.psy);
+ }
+
+ di->old_vbat = di->vbat;
+
+ /*
+ * No need to check the battery voltage every second when not close to
+ * the threshold.
+ */
+ if (di->vbat < (VBAT_TRESH_IP_CUR_RED + 100) &&
+ (di->vbat > (VBAT_TRESH_IP_CUR_RED - 100)))
+ t = 1;
+
+ queue_delayed_work(di->charger_wq, &di->check_vbat_work, t * HZ);
+}
+
+/**
+ * ab8500_charger_check_hw_failure_work() - check main charger failure
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the main charger status
+ */
+static void ab8500_charger_check_hw_failure_work(struct work_struct *work)
+{
+ int ret;
+ u8 reg_value;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, check_hw_failure_work.work);
+
+ /* Check if the status bits for HW failure is still active */
+ if (di->flags.mainextchnotok) {
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CH_STATUS2_REG, &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return;
+ }
+ if (!(reg_value & MAIN_CH_NOK)) {
+ di->flags.mainextchnotok = false;
+ ab8500_power_supply_changed(di, &di->ac_chg.psy);
+ }
+ }
+ if (di->flags.vbus_ovv) {
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG,
+ &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return;
+ }
+ if (!(reg_value & VBUS_OVV_TH)) {
+ di->flags.vbus_ovv = false;
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+ }
+ }
+ /* If we still have a failure, schedule a new check */
+ if (di->flags.mainextchnotok || di->flags.vbus_ovv) {
+ queue_delayed_work(di->charger_wq,
+ &di->check_hw_failure_work, round_jiffies(HZ));
+ }
+}
+
+/**
+ * ab8500_charger_kick_watchdog_work() - kick the watchdog
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for kicking the charger watchdog.
+ *
+ * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
+ * logic. That means we have to continously kick the charger
+ * watchdog even when no charger is connected. This is only
+ * valid once the AC charger has been enabled. This is
+ * a bug that is not handled by the algorithm and the
+ * watchdog have to be kicked by the charger driver
+ * when the AC charger is disabled
+ */
+static void ab8500_charger_kick_watchdog_work(struct work_struct *work)
+{
+ int ret;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, kick_wd_work.work);
+
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
+ if (ret)
+ dev_err(di->dev, "Failed to kick WD!\n");
+
+ /* Schedule a new watchdog kick */
+ queue_delayed_work(di->charger_wq,
+ &di->kick_wd_work, round_jiffies(WD_KICK_INTERVAL));
+}
+
+/**
+ * ab8500_charger_ac_work() - work to get and set main charger status
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the main charger status
+ */
+static void ab8500_charger_ac_work(struct work_struct *work)
+{
+ int ret;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, ac_work);
+
+ /*
+ * Since we can't be sure that the events are received
+ * synchronously, we have the check if the main charger is
+ * connected by reading the status register
+ */
+ ret = ab8500_charger_detect_chargers(di);
+ if (ret < 0)
+ return;
+
+ if (ret & AC_PW_CONN) {
+ di->ac.charger_connected = 1;
+ di->ac_conn = true;
+ } else {
+ di->ac.charger_connected = 0;
+ }
+
+ ab8500_power_supply_changed(di, &di->ac_chg.psy);
+ sysfs_notify(&di->ac_chg.psy.dev->kobj, NULL, "present");
+}
+
+/**
+ * ab8500_charger_detect_usb_type_work() - work to detect USB type
+ * @work: Pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
+{
+ int ret;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, detect_usb_type_work);
+
+ /*
+ * Since we can't be sure that the events are received
+ * synchronously, we have the check if is
+ * connected by reading the status register
+ */
+ ret = ab8500_charger_detect_chargers(di);
+ if (ret < 0)
+ return;
+
+ if (!(ret & USB_PW_CONN)) {
+ di->vbus_detected = 0;
+ ab8500_charger_set_usb_connected(di, false);
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+ } else {
+ di->vbus_detected = 1;
+
+ if (is_ab8500_1p1_or_earlier(di->parent)) {
+ ret = ab8500_charger_detect_usb_type(di);
+ if (!ret) {
+ ab8500_charger_set_usb_connected(di, true);
+ ab8500_power_supply_changed(di,
+ &di->usb_chg.psy);
+ }
+ } else {
+ /* For ABB cut2.0 and onwards we have an IRQ,
+ * USB_LINK_STATUS that will be triggered when the USB
+ * link status changes. The exception is USB connected
+ * during startup. Then we don't get a
+ * USB_LINK_STATUS IRQ
+ */
+ if (di->vbus_detected_start) {
+ di->vbus_detected_start = false;
+ ret = ab8500_charger_detect_usb_type(di);
+ if (!ret) {
+ ab8500_charger_set_usb_connected(di,
+ true);
+ ab8500_power_supply_changed(di,
+ &di->usb_chg.psy);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * ab8500_charger_usb_link_status_work() - work to detect USB type
+ * @work: pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+static void ab8500_charger_usb_link_status_work(struct work_struct *work)
+{
+ int ret;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, usb_link_status_work);
+
+ /*
+ * Since we can't be sure that the events are received
+ * synchronously, we have the check if is
+ * connected by reading the status register
+ */
+ ret = ab8500_charger_detect_chargers(di);
+ if (ret < 0)
+ return;
+
+ if (!(ret & USB_PW_CONN)) {
+ di->vbus_detected = 0;
+ ab8500_charger_set_usb_connected(di, false);
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+ } else {
+ di->vbus_detected = 1;
+ ret = ab8500_charger_read_usb_type(di);
+ if (!ret) {
+ /* Update maximum input current */
+ ret = ab8500_charger_set_vbus_in_curr(di,
+ di->max_usb_in_curr);
+ if (ret)
+ return;
+
+ ab8500_charger_set_usb_connected(di, true);
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+ } else if (ret == -ENXIO) {
+ /* No valid charger type detected */
+ ab8500_charger_set_usb_connected(di, false);
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+ }
+ }
+}
+
+static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
+{
+ int ret;
+ unsigned long flags;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, usb_state_changed_work);
+
+ if (!di->vbus_detected)
+ return;
+
+ spin_lock_irqsave(&di->usb_state.usb_lock, flags);
+ di->usb_state.usb_changed = false;
+ spin_unlock_irqrestore(&di->usb_state.usb_lock, flags);
+
+ /*
+ * wait for some time until you get updates from the usb stack
+ * and negotiations are completed
+ */
+ msleep(250);
+
+ if (di->usb_state.usb_changed)
+ return;
+
+ dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
+ __func__, di->usb_state.state, di->usb_state.usb_current);
+
+ switch (di->usb_state.state) {
+ case AB8500_BM_USB_STATE_RESET_HS:
+ case AB8500_BM_USB_STATE_RESET_FS:
+ case AB8500_BM_USB_STATE_SUSPEND:
+ case AB8500_BM_USB_STATE_MAX:
+ ab8500_charger_set_usb_connected(di, false);
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+ break;
+
+ case AB8500_BM_USB_STATE_RESUME:
+ /*
+ * when suspend->resume there should be delay
+ * of 1sec for enabling charging
+ */
+ msleep(1000);
+ /* Intentional fall through */
+ case AB8500_BM_USB_STATE_CONFIGURED:
+ /*
+ * USB is configured, enable charging with the charging
+ * input current obtained from USB driver
+ */
+ if (!ab8500_charger_get_usb_cur(di)) {
+ /* Update maximum input current */
+ ret = ab8500_charger_set_vbus_in_curr(di,
+ di->max_usb_in_curr);
+ if (ret)
+ return;
+
+ ab8500_charger_set_usb_connected(di, true);
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+ }
+ break;
+
+ default:
+ break;
+ };
+}
+
+/**
+ * ab8500_charger_check_usbchargernotok_work() - check USB chg not ok status
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the USB charger Not OK status
+ */
+static void ab8500_charger_check_usbchargernotok_work(struct work_struct *work)
+{
+ int ret;
+ u8 reg_value;
+ bool prev_status;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, check_usbchgnotok_work.work);
+
+ /* Check if the status bit for usbchargernotok is still active */
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG, &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return;
+ }
+ prev_status = di->flags.usbchargernotok;
+
+ if (reg_value & VBUS_CH_NOK) {
+ di->flags.usbchargernotok = true;
+ /* Check again in 1sec */
+ queue_delayed_work(di->charger_wq,
+ &di->check_usbchgnotok_work, HZ);
+ } else {
+ di->flags.usbchargernotok = false;
+ di->flags.vbus_collapse = false;
+ }
+
+ if (prev_status != di->flags.usbchargernotok)
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+}
+
+/**
+ * ab8500_charger_check_main_thermal_prot_work() - check main thermal status
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the Main thermal prot status
+ */
+static void ab8500_charger_check_main_thermal_prot_work(
+ struct work_struct *work)
+{
+ int ret;
+ u8 reg_value;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, check_main_thermal_prot_work);
+
+ /* Check if the status bit for main_thermal_prot is still active */
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CH_STATUS2_REG, &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return;
+ }
+ if (reg_value & MAIN_CH_TH_PROT)
+ di->flags.main_thermal_prot = true;
+ else
+ di->flags.main_thermal_prot = false;
+
+ ab8500_power_supply_changed(di, &di->ac_chg.psy);
+}
+
+/**
+ * ab8500_charger_check_usb_thermal_prot_work() - check usb thermal status
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the USB thermal prot status
+ */
+static void ab8500_charger_check_usb_thermal_prot_work(
+ struct work_struct *work)
+{
+ int ret;
+ u8 reg_value;
+
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, check_usb_thermal_prot_work);
+
+ /* Check if the status bit for usb_thermal_prot is still active */
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG, &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return;
+ }
+ if (reg_value & USB_CH_TH_PROT)
+ di->flags.usb_thermal_prot = true;
+ else
+ di->flags.usb_thermal_prot = false;
+
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+}
+
+/**
+ * ab8500_charger_mainchunplugdet_handler() - main charger unplugged
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchunplugdet_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "Main charger unplugged\n");
+ queue_work(di->charger_wq, &di->ac_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainchplugdet_handler() - main charger plugged
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchplugdet_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "Main charger plugged\n");
+ queue_work(di->charger_wq, &di->ac_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainextchnotok_handler() - main charger not ok
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainextchnotok_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "Main charger not ok\n");
+ di->flags.mainextchnotok = true;
+ ab8500_power_supply_changed(di, &di->ac_chg.psy);
+
+ /* Schedule a new HW failure check */
+ queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainchthprotr_handler() - Die temp is above main charger
+ * thermal protection threshold
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchthprotr_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev,
+ "Die temp above Main charger thermal protection threshold\n");
+ queue_work(di->charger_wq, &di->check_main_thermal_prot_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainchthprotf_handler() - Die temp is below main charger
+ * thermal protection threshold
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchthprotf_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev,
+ "Die temp ok for Main charger thermal protection threshold\n");
+ queue_work(di->charger_wq, &di->check_main_thermal_prot_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_vbusdetf_handler() - VBUS falling detected
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbusdetf_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "VBUS falling detected\n");
+ queue_work(di->charger_wq, &di->detect_usb_type_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_vbusdetr_handler() - VBUS rising detected
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbusdetr_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ di->vbus_detected = true;
+ dev_dbg(di->dev, "VBUS rising detected\n");
+ queue_work(di->charger_wq, &di->detect_usb_type_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usblinkstatus_handler() - USB link status has changed
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usblinkstatus_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "USB link status changed\n");
+
+ queue_work(di->charger_wq, &di->usb_link_status_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usbchthprotr_handler() - Die temp is above usb charger
+ * thermal protection threshold
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usbchthprotr_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev,
+ "Die temp above USB charger thermal protection threshold\n");
+ queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usbchthprotf_handler() - Die temp is below usb charger
+ * thermal protection threshold
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usbchthprotf_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev,
+ "Die temp ok for USB charger thermal protection threshold\n");
+ queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usbchargernotokr_handler() - USB charger not ok detected
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usbchargernotokr_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "Not allowed USB charger detected\n");
+ queue_delayed_work(di->charger_wq, &di->check_usbchgnotok_work, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_chwdexp_handler() - Charger watchdog expired
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_chwdexp_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "Charger watchdog expired\n");
+
+ /*
+ * The charger that was online when the watchdog expired
+ * needs to be restarted for charging to start again
+ */
+ if (di->ac.charger_online) {
+ di->ac.wd_expired = true;
+ ab8500_power_supply_changed(di, &di->ac_chg.psy);
+ }
+ if (di->usb.charger_online) {
+ di->usb.wd_expired = true;
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_vbusovv_handler() - VBUS overvoltage detected
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbusovv_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "VBUS overvoltage detected\n");
+ di->flags.vbus_ovv = true;
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+
+ /* Schedule a new HW failure check */
+ queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_ac_get_property() - get the ac/mains properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the ac/mains
+ * properties by reading the sysfs files.
+ * AC/Mains properties are online, present and voltage.
+ * online: ac/mains charging is in progress or not
+ * present: presence of the ac/mains
+ * voltage: AC/Mains voltage
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_ac_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ab8500_charger *di;
+
+ di = to_ab8500_charger_ac_device_info(psy_to_ux500_charger(psy));
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (di->flags.mainextchnotok)
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ else if (di->ac.wd_expired || di->usb.wd_expired)
+ val->intval = POWER_SUPPLY_HEALTH_DEAD;
+ else if (di->flags.main_thermal_prot)
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = di->ac.charger_online;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = di->ac.charger_connected;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ di->ac.charger_voltage = ab8500_charger_get_ac_voltage(di);
+ val->intval = di->ac.charger_voltage * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ /*
+ * This property is used to indicate when CV mode is entered
+ * for the AC charger
+ */
+ di->ac.cv_active = ab8500_charger_ac_cv(di);
+ val->intval = di->ac.cv_active;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = ab8500_charger_get_ac_current(di) * 1000;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * ab8500_charger_usb_get_property() - get the usb properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the usb
+ * properties by reading the sysfs files.
+ * USB properties are online, present and voltage.
+ * online: usb charging is in progress or not
+ * present: presence of the usb
+ * voltage: vbus voltage
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ab8500_charger *di;
+
+ di = to_ab8500_charger_usb_device_info(psy_to_ux500_charger(psy));
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (di->flags.usbchargernotok)
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ else if (di->ac.wd_expired || di->usb.wd_expired)
+ val->intval = POWER_SUPPLY_HEALTH_DEAD;
+ else if (di->flags.usb_thermal_prot)
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (di->flags.vbus_ovv)
+ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = di->usb.charger_online;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = di->usb.charger_connected;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ di->usb.charger_voltage = ab8500_charger_get_vbus_voltage(di);
+ val->intval = di->usb.charger_voltage * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ /*
+ * This property is used to indicate when CV mode is entered
+ * for the USB charger
+ */
+ di->usb.cv_active = ab8500_charger_usb_cv(di);
+ val->intval = di->usb.cv_active;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = ab8500_charger_get_usb_current(di) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ /*
+ * This property is used to indicate when VBUS has collapsed
+ * due to too high output current from the USB charger
+ */
+ if (di->flags.vbus_collapse)
+ val->intval = 1;
+ else
+ val->intval = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * ab8500_charger_init_hw_registers() - Set up charger related registers
+ * @di: pointer to the ab8500_charger structure
+ *
+ * Set up charger OVV, watchdog and maximum voltage registers as well as
+ * charging of the backup battery
+ */
+static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
+{
+ int ret = 0;
+
+ /* Setup maximum charger current and voltage for ABB cut2.0 */
+ if (!is_ab8500_1p1_or_earlier(di->parent)) {
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_CH_VOLT_LVL_MAX_REG, CH_VOL_LVL_4P6);
+ if (ret) {
+ dev_err(di->dev,
+ "failed to set CH_VOLT_LVL_MAX_REG\n");
+ goto out;
+ }
+
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_CH_OPT_CRNTLVL_MAX_REG, CH_OP_CUR_LVL_1P6);
+ if (ret) {
+ dev_err(di->dev,
+ "failed to set CH_OPT_CRNTLVL_MAX_REG\n");
+ goto out;
+ }
+ }
+
+ /* VBUS OVV set to 6.3V and enable automatic current limitiation */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_USBCH_CTRL2_REG,
+ VBUS_OVV_SELECT_6P3V | VBUS_AUTO_IN_CURR_LIM_ENA);
+ if (ret) {
+ dev_err(di->dev, "failed to set VBUS OVV\n");
+ goto out;
+ }
+
+ /* Enable main watchdog in OTP */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_OTP_EMUL, AB8500_OTP_CONF_15, OTP_ENABLE_WD);
+ if (ret) {
+ dev_err(di->dev, "failed to enable main WD in OTP\n");
+ goto out;
+ }
+
+ /* Enable main watchdog */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_SYS_CTRL2_BLOCK,
+ AB8500_MAIN_WDOG_CTRL_REG, MAIN_WDOG_ENA);
+ if (ret) {
+ dev_err(di->dev, "faile to enable main watchdog\n");
+ goto out;
+ }
+
+ /*
+ * Due to internal synchronisation, Enable and Kick watchdog bits
+ * cannot be enabled in a single write.
+ * A minimum delay of 2*32 kHz period (62.5µs) must be inserted
+ * between writing Enable then Kick bits.
+ */
+ udelay(63);
+
+ /* Kick main watchdog */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_SYS_CTRL2_BLOCK,
+ AB8500_MAIN_WDOG_CTRL_REG,
+ (MAIN_WDOG_ENA | MAIN_WDOG_KICK));
+ if (ret) {
+ dev_err(di->dev, "failed to kick main watchdog\n");
+ goto out;
+ }
+
+ /* Disable main watchdog */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_SYS_CTRL2_BLOCK,
+ AB8500_MAIN_WDOG_CTRL_REG, MAIN_WDOG_DIS);
+ if (ret) {
+ dev_err(di->dev, "failed to disable main watchdog\n");
+ goto out;
+ }
+
+ /* Set watchdog timeout */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_WD_TIMER_REG, WD_TIMER);
+ if (ret) {
+ dev_err(di->dev, "failed to set charger watchdog timeout\n");
+ goto out;
+ }
+
+ /* Backup battery voltage and current */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_RTC,
+ AB8500_RTC_BACKUP_CHG_REG,
+ di->bat->bkup_bat_v |
+ di->bat->bkup_bat_i);
+ if (ret) {
+ dev_err(di->dev, "failed to setup backup battery charging\n");
+ goto out;
+ }
+
+ /* Enable backup battery charging */
+ abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_RTC, AB8500_RTC_CTRL_REG,
+ RTC_BUP_CH_ENA, RTC_BUP_CH_ENA);
+ if (ret < 0)
+ dev_err(di->dev, "%s mask and set failed\n", __func__);
+
+out:
+ return ret;
+}
+
+/*
+ * ab8500 charger driver interrupts and their respective isr
+ */
+static struct ab8500_charger_interrupts ab8500_charger_irq[] = {
+ {"MAIN_CH_UNPLUG_DET", ab8500_charger_mainchunplugdet_handler},
+ {"MAIN_CHARGE_PLUG_DET", ab8500_charger_mainchplugdet_handler},
+ {"MAIN_EXT_CH_NOT_OK", ab8500_charger_mainextchnotok_handler},
+ {"MAIN_CH_TH_PROT_R", ab8500_charger_mainchthprotr_handler},
+ {"MAIN_CH_TH_PROT_F", ab8500_charger_mainchthprotf_handler},
+ {"VBUS_DET_F", ab8500_charger_vbusdetf_handler},
+ {"VBUS_DET_R", ab8500_charger_vbusdetr_handler},
+ {"USB_LINK_STATUS", ab8500_charger_usblinkstatus_handler},
+ {"USB_CH_TH_PROT_R", ab8500_charger_usbchthprotr_handler},
+ {"USB_CH_TH_PROT_F", ab8500_charger_usbchthprotf_handler},
+ {"USB_CHARGER_NOT_OKR", ab8500_charger_usbchargernotokr_handler},
+ {"VBUS_OVV", ab8500_charger_vbusovv_handler},
+ {"CH_WD_EXP", ab8500_charger_chwdexp_handler},
+};
+
+static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
+ unsigned long event, void *power)
+{
+ struct ab8500_charger *di =
+ container_of(nb, struct ab8500_charger, nb);
+ enum ab8500_usb_state bm_usb_state;
+ unsigned mA = *((unsigned *)power);
+
+ if (event != USB_EVENT_VBUS) {
+ dev_dbg(di->dev, "not a standard host, returning\n");
+ return NOTIFY_DONE;
+ }
+
+ /* TODO: State is fabricate here. See if charger really needs USB
+ * state or if mA is enough
+ */
+ if ((di->usb_state.usb_current == 2) && (mA > 2))
+ bm_usb_state = AB8500_BM_USB_STATE_RESUME;
+ else if (mA == 0)
+ bm_usb_state = AB8500_BM_USB_STATE_RESET_HS;
+ else if (mA == 2)
+ bm_usb_state = AB8500_BM_USB_STATE_SUSPEND;
+ else if (mA >= 8) /* 8, 100, 500 */
+ bm_usb_state = AB8500_BM_USB_STATE_CONFIGURED;
+ else /* Should never occur */
+ bm_usb_state = AB8500_BM_USB_STATE_RESET_FS;
+
+ dev_dbg(di->dev, "%s usb_state: 0x%02x mA: %d\n",
+ __func__, bm_usb_state, mA);
+
+ spin_lock(&di->usb_state.usb_lock);
+ di->usb_state.usb_changed = true;
+ spin_unlock(&di->usb_state.usb_lock);
+
+ di->usb_state.state = bm_usb_state;
+ di->usb_state.usb_current = mA;
+
+ queue_work(di->charger_wq, &di->usb_state_changed_work);
+
+ return NOTIFY_OK;
+}
+
+#if defined(CONFIG_PM)
+static int ab8500_charger_resume(struct platform_device *pdev)
+{
+ int ret;
+ struct ab8500_charger *di = platform_get_drvdata(pdev);
+
+ /*
+ * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
+ * logic. That means we have to continously kick the charger
+ * watchdog even when no charger is connected. This is only
+ * valid once the AC charger has been enabled. This is
+ * a bug that is not handled by the algorithm and the
+ * watchdog have to be kicked by the charger driver
+ * when the AC charger is disabled
+ */
+ if (di->ac_conn && is_ab8500_1p1_or_earlier(di->parent)) {
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
+ if (ret)
+ dev_err(di->dev, "Failed to kick WD!\n");
+
+ /* If not already pending start a new timer */
+ if (!delayed_work_pending(
+ &di->kick_wd_work)) {
+ queue_delayed_work(di->charger_wq, &di->kick_wd_work,
+ round_jiffies(WD_KICK_INTERVAL));
+ }
+ }
+
+ /* If we still have a HW failure, schedule a new check */
+ if (di->flags.mainextchnotok || di->flags.vbus_ovv) {
+ queue_delayed_work(di->charger_wq,
+ &di->check_hw_failure_work, 0);
+ }
+
+ return 0;
+}
+
+static int ab8500_charger_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct ab8500_charger *di = platform_get_drvdata(pdev);
+
+ /* Cancel any pending HW failure check */
+ if (delayed_work_pending(&di->check_hw_failure_work))
+ cancel_delayed_work(&di->check_hw_failure_work);
+
+ return 0;
+}
+#else
+#define ab8500_charger_suspend NULL
+#define ab8500_charger_resume NULL
+#endif
+
+static int __devexit ab8500_charger_remove(struct platform_device *pdev)
+{
+ struct ab8500_charger *di = platform_get_drvdata(pdev);
+ int i, irq, ret;
+
+ /* Disable AC charging */
+ ab8500_charger_ac_en(&di->ac_chg, false, 0, 0);
+
+ /* Disable USB charging */
+ ab8500_charger_usb_en(&di->usb_chg, false, 0, 0);
+
+ /* Disable interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
+ free_irq(irq, di);
+ }
+
+ /* disable the regulator */
+ regulator_put(di->regu);
+
+ /* Backup battery voltage and current disable */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, 0);
+ if (ret < 0)
+ dev_err(di->dev, "%s mask and set failed\n", __func__);
+
+ usb_unregister_notifier(di->usb_phy, &di->nb);
+ usb_put_transceiver(di->usb_phy);
+
+ /* Delete the work queue */
+ destroy_workqueue(di->charger_wq);
+
+ flush_scheduled_work();
+ power_supply_unregister(&di->usb_chg.psy);
+ power_supply_unregister(&di->ac_chg.psy);
+ platform_set_drvdata(pdev, NULL);
+ kfree(di);
+
+ return 0;
+}
+
+static int __devinit ab8500_charger_probe(struct platform_device *pdev)
+{
+ int irq, i, charger_status, ret = 0;
+ struct abx500_bm_plat_data *plat_data;
+
+ struct ab8500_charger *di =
+ kzalloc(sizeof(struct ab8500_charger), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ /* get parent data */
+ di->dev = &pdev->dev;
+ di->parent = dev_get_drvdata(pdev->dev.parent);
+ di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+
+ /* initialize lock */
+ spin_lock_init(&di->usb_state.usb_lock);
+
+ /* get charger specific platform data */
+ plat_data = pdev->dev.platform_data;
+ di->pdata = plat_data->charger;
+
+ if (!di->pdata) {
+ dev_err(di->dev, "no charger platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ /* get battery specific platform data */
+ di->bat = plat_data->battery;
+ if (!di->bat) {
+ dev_err(di->dev, "no battery platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ di->autopower = false;
+
+ /* AC supply */
+ /* power_supply base class */
+ di->ac_chg.psy.name = "ab8500_ac";
+ di->ac_chg.psy.type = POWER_SUPPLY_TYPE_MAINS;
+ di->ac_chg.psy.properties = ab8500_charger_ac_props;
+ di->ac_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_ac_props);
+ di->ac_chg.psy.get_property = ab8500_charger_ac_get_property;
+ di->ac_chg.psy.supplied_to = di->pdata->supplied_to;
+ di->ac_chg.psy.num_supplicants = di->pdata->num_supplicants;
+ /* ux500_charger sub-class */
+ di->ac_chg.ops.enable = &ab8500_charger_ac_en;
+ di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
+ di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current;
+ di->ac_chg.max_out_volt = ab8500_charger_voltage_map[
+ ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
+ di->ac_chg.max_out_curr = ab8500_charger_current_map[
+ ARRAY_SIZE(ab8500_charger_current_map) - 1];
+
+ /* USB supply */
+ /* power_supply base class */
+ di->usb_chg.psy.name = "ab8500_usb";
+ di->usb_chg.psy.type = POWER_SUPPLY_TYPE_USB;
+ di->usb_chg.psy.properties = ab8500_charger_usb_props;
+ di->usb_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_usb_props);
+ di->usb_chg.psy.get_property = ab8500_charger_usb_get_property;
+ di->usb_chg.psy.supplied_to = di->pdata->supplied_to;
+ di->usb_chg.psy.num_supplicants = di->pdata->num_supplicants;
+ /* ux500_charger sub-class */
+ di->usb_chg.ops.enable = &ab8500_charger_usb_en;
+ di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
+ di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current;
+ di->usb_chg.max_out_volt = ab8500_charger_voltage_map[
+ ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
+ di->usb_chg.max_out_curr = ab8500_charger_current_map[
+ ARRAY_SIZE(ab8500_charger_current_map) - 1];
+
+
+ /* Create a work queue for the charger */
+ di->charger_wq =
+ create_singlethread_workqueue("ab8500_charger_wq");
+ if (di->charger_wq == NULL) {
+ dev_err(di->dev, "failed to create work queue\n");
+ goto free_device_info;
+ }
+
+ /* Init work for HW failure check */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->check_hw_failure_work,
+ ab8500_charger_check_hw_failure_work);
+ INIT_DELAYED_WORK_DEFERRABLE(&di->check_usbchgnotok_work,
+ ab8500_charger_check_usbchargernotok_work);
+
+ /*
+ * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
+ * logic. That means we have to continously kick the charger
+ * watchdog even when no charger is connected. This is only
+ * valid once the AC charger has been enabled. This is
+ * a bug that is not handled by the algorithm and the
+ * watchdog have to be kicked by the charger driver
+ * when the AC charger is disabled
+ */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->kick_wd_work,
+ ab8500_charger_kick_watchdog_work);
+
+ INIT_DELAYED_WORK_DEFERRABLE(&di->check_vbat_work,
+ ab8500_charger_check_vbat_work);
+
+ /* Init work for charger detection */
+ INIT_WORK(&di->usb_link_status_work,
+ ab8500_charger_usb_link_status_work);
+ INIT_WORK(&di->ac_work, ab8500_charger_ac_work);
+ INIT_WORK(&di->detect_usb_type_work,
+ ab8500_charger_detect_usb_type_work);
+
+ INIT_WORK(&di->usb_state_changed_work,
+ ab8500_charger_usb_state_changed_work);
+
+ /* Init work for checking HW status */
+ INIT_WORK(&di->check_main_thermal_prot_work,
+ ab8500_charger_check_main_thermal_prot_work);
+ INIT_WORK(&di->check_usb_thermal_prot_work,
+ ab8500_charger_check_usb_thermal_prot_work);
+
+ /*
+ * VDD ADC supply needs to be enabled from this driver when there
+ * is a charger connected to avoid erroneous BTEMP_HIGH/LOW
+ * interrupts during charging
+ */
+ di->regu = regulator_get(di->dev, "vddadc");
+ if (IS_ERR(di->regu)) {
+ ret = PTR_ERR(di->regu);
+ dev_err(di->dev, "failed to get vddadc regulator\n");
+ goto free_charger_wq;
+ }
+
+
+ /* Initialize OVV, and other registers */
+ ret = ab8500_charger_init_hw_registers(di);
+ if (ret) {
+ dev_err(di->dev, "failed to initialize ABB registers\n");
+ goto free_regulator;
+ }
+
+ /* Register AC charger class */
+ ret = power_supply_register(di->dev, &di->ac_chg.psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register AC charger\n");
+ goto free_regulator;
+ }
+
+ /* Register USB charger class */
+ ret = power_supply_register(di->dev, &di->usb_chg.psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register USB charger\n");
+ goto free_ac;
+ }
+
+ di->usb_phy = usb_get_transceiver();
+ if (!di->usb_phy) {
+ dev_err(di->dev, "failed to get usb transceiver\n");
+ ret = -EINVAL;
+ goto free_usb;
+ }
+ di->nb.notifier_call = ab8500_charger_usb_notifier_call;
+ ret = usb_register_notifier(di->usb_phy, &di->nb);
+ if (ret) {
+ dev_err(di->dev, "failed to register usb notifier\n");
+ goto put_usb_phy;
+ }
+
+ /* Identify the connected charger types during startup */
+ charger_status = ab8500_charger_detect_chargers(di);
+ if (charger_status & AC_PW_CONN) {
+ di->ac.charger_connected = 1;
+ di->ac_conn = true;
+ ab8500_power_supply_changed(di, &di->ac_chg.psy);
+ sysfs_notify(&di->ac_chg.psy.dev->kobj, NULL, "present");
+ }
+
+ if (charger_status & USB_PW_CONN) {
+ dev_dbg(di->dev, "VBUS Detect during startup\n");
+ di->vbus_detected = true;
+ di->vbus_detected_start = true;
+ queue_work(di->charger_wq,
+ &di->detect_usb_type_work);
+ }
+
+ /* Register interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
+ ret = request_threaded_irq(irq, NULL, ab8500_charger_irq[i].isr,
+ IRQF_SHARED | IRQF_NO_SUSPEND,
+ ab8500_charger_irq[i].name, di);
+
+ if (ret != 0) {
+ dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+ , ab8500_charger_irq[i].name, irq, ret);
+ goto free_irq;
+ }
+ dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+ ab8500_charger_irq[i].name, irq, ret);
+ }
+
+ platform_set_drvdata(pdev, di);
+
+ return ret;
+
+free_irq:
+ usb_unregister_notifier(di->usb_phy, &di->nb);
+
+ /* We also have to free all successfully registered irqs */
+ for (i = i - 1; i >= 0; i--) {
+ irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
+ free_irq(irq, di);
+ }
+put_usb_phy:
+ usb_put_transceiver(di->usb_phy);
+free_usb:
+ power_supply_unregister(&di->usb_chg.psy);
+free_ac:
+ power_supply_unregister(&di->ac_chg.psy);
+free_regulator:
+ regulator_put(di->regu);
+free_charger_wq:
+ destroy_workqueue(di->charger_wq);
+free_device_info:
+ kfree(di);
+
+ return ret;
+}
+
+static struct platform_driver ab8500_charger_driver = {
+ .probe = ab8500_charger_probe,
+ .remove = __devexit_p(ab8500_charger_remove),
+ .suspend = ab8500_charger_suspend,
+ .resume = ab8500_charger_resume,
+ .driver = {
+ .name = "ab8500-charger",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab8500_charger_init(void)
+{
+ return platform_driver_register(&ab8500_charger_driver);
+}
+
+static void __exit ab8500_charger_exit(void)
+{
+ platform_driver_unregister(&ab8500_charger_driver);
+}
+
+subsys_initcall_sync(ab8500_charger_init);
+module_exit(ab8500_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
+MODULE_ALIAS("platform:ab8500-charger");
+MODULE_DESCRIPTION("AB8500 charger management driver");
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
new file mode 100644
index 0000000..c22f2f0
--- /dev/null
+++ b/drivers/power/ab8500_fg.c
@@ -0,0 +1,2637 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2012
+ *
+ * Main and Back-up battery management driver.
+ *
+ * Note: Backup battery management is required in case of Li-Ion battery and not
+ * for capacitive battery. HREF boards have capacitive battery and hence backup
+ * battery management is not used and the supported code is available in this
+ * driver.
+ *
+ * License Terms: GNU General Public License v2
+ * Author:
+ * Johan Palsson <johan.palsson@stericsson.com>
+ * Karl Komierowski <karl.komierowski@stericsson.com>
+ * Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/kobject.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/slab.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/delay.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/mfd/abx500.h>
+#include <linux/time.h>
+#include <linux/completion.h>
+
+#define MILLI_TO_MICRO 1000
+#define FG_LSB_IN_MA 1627
+#define QLSB_NANO_AMP_HOURS_X10 1129
+#define INS_CURR_TIMEOUT (3 * HZ)
+
+#define SEC_TO_SAMPLE(S) (S * 4)
+
+#define NBR_AVG_SAMPLES 20
+
+#define LOW_BAT_CHECK_INTERVAL (2 * HZ)
+
+#define VALID_CAPACITY_SEC (45 * 60) /* 45 minutes */
+#define BATT_OK_MIN 2360 /* mV */
+#define BATT_OK_INCREMENT 50 /* mV */
+#define BATT_OK_MAX_NR_INCREMENTS 0xE
+
+/* FG constants */
+#define BATT_OVV 0x01
+
+#define interpolate(x, x1, y1, x2, y2) \
+ ((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
+
+#define to_ab8500_fg_device_info(x) container_of((x), \
+ struct ab8500_fg, fg_psy);
+
+/**
+ * struct ab8500_fg_interrupts - ab8500 fg interupts
+ * @name: name of the interrupt
+ * @isr function pointer to the isr
+ */
+struct ab8500_fg_interrupts {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+enum ab8500_fg_discharge_state {
+ AB8500_FG_DISCHARGE_INIT,
+ AB8500_FG_DISCHARGE_INITMEASURING,
+ AB8500_FG_DISCHARGE_INIT_RECOVERY,
+ AB8500_FG_DISCHARGE_RECOVERY,
+ AB8500_FG_DISCHARGE_READOUT_INIT,
+ AB8500_FG_DISCHARGE_READOUT,
+ AB8500_FG_DISCHARGE_WAKEUP,
+};
+
+static char *discharge_state[] = {
+ "DISCHARGE_INIT",
+ "DISCHARGE_INITMEASURING",
+ "DISCHARGE_INIT_RECOVERY",
+ "DISCHARGE_RECOVERY",
+ "DISCHARGE_READOUT_INIT",
+ "DISCHARGE_READOUT",
+ "DISCHARGE_WAKEUP",
+};
+
+enum ab8500_fg_charge_state {
+ AB8500_FG_CHARGE_INIT,
+ AB8500_FG_CHARGE_READOUT,
+};
+
+static char *charge_state[] = {
+ "CHARGE_INIT",
+ "CHARGE_READOUT",
+};
+
+enum ab8500_fg_calibration_state {
+ AB8500_FG_CALIB_INIT,
+ AB8500_FG_CALIB_WAIT,
+ AB8500_FG_CALIB_END,
+};
+
+struct ab8500_fg_avg_cap {
+ int avg;
+ int samples[NBR_AVG_SAMPLES];
+ __kernel_time_t time_stamps[NBR_AVG_SAMPLES];
+ int pos;
+ int nbr_samples;
+ int sum;
+};
+
+struct ab8500_fg_battery_capacity {
+ int max_mah_design;
+ int max_mah;
+ int mah;
+ int permille;
+ int level;
+ int prev_mah;
+ int prev_percent;
+ int prev_level;
+ int user_mah;
+};
+
+struct ab8500_fg_flags {
+ bool fg_enabled;
+ bool conv_done;
+ bool charging;
+ bool fully_charged;
+ bool force_full;
+ bool low_bat_delay;
+ bool low_bat;
+ bool bat_ovv;
+ bool batt_unknown;
+ bool calibrate;
+ bool user_cap;
+ bool batt_id_received;
+};
+
+struct inst_curr_result_list {
+ struct list_head list;
+ int *result;
+};
+
+/**
+ * struct ab8500_fg - ab8500 FG device information
+ * @dev: Pointer to the structure device
+ * @node: a list of AB8500 FGs, hence prepared for reentrance
+ * @irq holds the CCEOC interrupt number
+ * @vbat: Battery voltage in mV
+ * @vbat_nom: Nominal battery voltage in mV
+ * @inst_curr: Instantenous battery current in mA
+ * @avg_curr: Average battery current in mA
+ * @bat_temp battery temperature
+ * @fg_samples: Number of samples used in the FG accumulation
+ * @accu_charge: Accumulated charge from the last conversion
+ * @recovery_cnt: Counter for recovery mode
+ * @high_curr_cnt: Counter for high current mode
+ * @init_cnt: Counter for init mode
+ * @recovery_needed: Indicate if recovery is needed
+ * @high_curr_mode: Indicate if we're in high current mode
+ * @init_capacity: Indicate if initial capacity measuring should be done
+ * @turn_off_fg: True if fg was off before current measurement
+ * @calib_state State during offset calibration
+ * @discharge_state: Current discharge state
+ * @charge_state: Current charge state
+ * @ab8500_fg_complete Completion struct used for the instant current reading
+ * @flags: Structure for information about events triggered
+ * @bat_cap: Structure for battery capacity specific parameters
+ * @avg_cap: Average capacity filter
+ * @parent: Pointer to the struct ab8500
+ * @gpadc: Pointer to the struct gpadc
+ * @pdata: Pointer to the abx500_fg platform data
+ * @bat: Pointer to the abx500_bm platform data
+ * @fg_psy: Structure that holds the FG specific battery properties
+ * @fg_wq: Work queue for running the FG algorithm
+ * @fg_periodic_work: Work to run the FG algorithm periodically
+ * @fg_low_bat_work: Work to check low bat condition
+ * @fg_reinit_work Work used to reset and reinitialise the FG algorithm
+ * @fg_work: Work to run the FG algorithm instantly
+ * @fg_acc_cur_work: Work to read the FG accumulator
+ * @fg_check_hw_failure_work: Work for checking HW state
+ * @cc_lock: Mutex for locking the CC
+ * @fg_kobject: Structure of type kobject
+ */
+struct ab8500_fg {
+ struct device *dev;
+ struct list_head node;
+ int irq;
+ int vbat;
+ int vbat_nom;
+ int inst_curr;
+ int avg_curr;
+ int bat_temp;
+ int fg_samples;
+ int accu_charge;
+ int recovery_cnt;
+ int high_curr_cnt;
+ int init_cnt;
+ bool recovery_needed;
+ bool high_curr_mode;
+ bool init_capacity;
+ bool turn_off_fg;
+ enum ab8500_fg_calibration_state calib_state;
+ enum ab8500_fg_discharge_state discharge_state;
+ enum ab8500_fg_charge_state charge_state;
+ struct completion ab8500_fg_complete;
+ struct ab8500_fg_flags flags;
+ struct ab8500_fg_battery_capacity bat_cap;
+ struct ab8500_fg_avg_cap avg_cap;
+ struct ab8500 *parent;
+ struct ab8500_gpadc *gpadc;
+ struct abx500_fg_platform_data *pdata;
+ struct abx500_bm_data *bat;
+ struct power_supply fg_psy;
+ struct workqueue_struct *fg_wq;
+ struct delayed_work fg_periodic_work;
+ struct delayed_work fg_low_bat_work;
+ struct delayed_work fg_reinit_work;
+ struct work_struct fg_work;
+ struct work_struct fg_acc_cur_work;
+ struct delayed_work fg_check_hw_failure_work;
+ struct mutex cc_lock;
+ struct kobject fg_kobject;
+};
+static LIST_HEAD(ab8500_fg_list);
+
+/**
+ * ab8500_fg_get() - returns a reference to the primary AB8500 fuel gauge
+ * (i.e. the first fuel gauge in the instance list)
+ */
+struct ab8500_fg *ab8500_fg_get(void)
+{
+ struct ab8500_fg *fg;
+
+ if (list_empty(&ab8500_fg_list))
+ return NULL;
+
+ fg = list_first_entry(&ab8500_fg_list, struct ab8500_fg, node);
+ return fg;
+}
+
+/* Main battery properties */
+static enum power_supply_property ab8500_fg_props[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+ POWER_SUPPLY_PROP_ENERGY_FULL,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+};
+
+/*
+ * This array maps the raw hex value to lowbat voltage used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_fg_lowbat_voltage_map[] = {
+ 2300 ,
+ 2325 ,
+ 2350 ,
+ 2375 ,
+ 2400 ,
+ 2425 ,
+ 2450 ,
+ 2475 ,
+ 2500 ,
+ 2525 ,
+ 2550 ,
+ 2575 ,
+ 2600 ,
+ 2625 ,
+ 2650 ,
+ 2675 ,
+ 2700 ,
+ 2725 ,
+ 2750 ,
+ 2775 ,
+ 2800 ,
+ 2825 ,
+ 2850 ,
+ 2875 ,
+ 2900 ,
+ 2925 ,
+ 2950 ,
+ 2975 ,
+ 3000 ,
+ 3025 ,
+ 3050 ,
+ 3075 ,
+ 3100 ,
+ 3125 ,
+ 3150 ,
+ 3175 ,
+ 3200 ,
+ 3225 ,
+ 3250 ,
+ 3275 ,
+ 3300 ,
+ 3325 ,
+ 3350 ,
+ 3375 ,
+ 3400 ,
+ 3425 ,
+ 3450 ,
+ 3475 ,
+ 3500 ,
+ 3525 ,
+ 3550 ,
+ 3575 ,
+ 3600 ,
+ 3625 ,
+ 3650 ,
+ 3675 ,
+ 3700 ,
+ 3725 ,
+ 3750 ,
+ 3775 ,
+ 3800 ,
+ 3825 ,
+ 3850 ,
+ 3850 ,
+};
+
+static u8 ab8500_volt_to_regval(int voltage)
+{
+ int i;
+
+ if (voltage < ab8500_fg_lowbat_voltage_map[0])
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_fg_lowbat_voltage_map); i++) {
+ if (voltage < ab8500_fg_lowbat_voltage_map[i])
+ return (u8) i - 1;
+ }
+
+ /* If not captured above, return index of last element */
+ return (u8) ARRAY_SIZE(ab8500_fg_lowbat_voltage_map) - 1;
+}
+
+/**
+ * ab8500_fg_is_low_curr() - Low or high current mode
+ * @di: pointer to the ab8500_fg structure
+ * @curr: the current to base or our decision on
+ *
+ * Low current mode if the current consumption is below a certain threshold
+ */
+static int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr)
+{
+ /*
+ * We want to know if we're in low current mode
+ */
+ if (curr > -di->bat->fg_params->high_curr_threshold)
+ return true;
+ else
+ return false;
+}
+
+/**
+ * ab8500_fg_add_cap_sample() - Add capacity to average filter
+ * @di: pointer to the ab8500_fg structure
+ * @sample: the capacity in mAh to add to the filter
+ *
+ * A capacity is added to the filter and a new mean capacity is calculated and
+ * returned
+ */
+static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample)
+{
+ struct timespec ts;
+ struct ab8500_fg_avg_cap *avg = &di->avg_cap;
+
+ getnstimeofday(&ts);
+
+ do {
+ avg->sum += sample - avg->samples[avg->pos];
+ avg->samples[avg->pos] = sample;
+ avg->time_stamps[avg->pos] = ts.tv_sec;
+ avg->pos++;
+
+ if (avg->pos == NBR_AVG_SAMPLES)
+ avg->pos = 0;
+
+ if (avg->nbr_samples < NBR_AVG_SAMPLES)
+ avg->nbr_samples++;
+
+ /*
+ * Check the time stamp for each sample. If too old,
+ * replace with latest sample
+ */
+ } while (ts.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]);
+
+ avg->avg = avg->sum / avg->nbr_samples;
+
+ return avg->avg;
+}
+
+/**
+ * ab8500_fg_clear_cap_samples() - Clear average filter
+ * @di: pointer to the ab8500_fg structure
+ *
+ * The capacity filter is is reset to zero.
+ */
+static void ab8500_fg_clear_cap_samples(struct ab8500_fg *di)
+{
+ int i;
+ struct ab8500_fg_avg_cap *avg = &di->avg_cap;
+
+ avg->pos = 0;
+ avg->nbr_samples = 0;
+ avg->sum = 0;
+ avg->avg = 0;
+
+ for (i = 0; i < NBR_AVG_SAMPLES; i++) {
+ avg->samples[i] = 0;
+ avg->time_stamps[i] = 0;
+ }
+}
+
+/**
+ * ab8500_fg_fill_cap_sample() - Fill average filter
+ * @di: pointer to the ab8500_fg structure
+ * @sample: the capacity in mAh to fill the filter with
+ *
+ * The capacity filter is filled with a capacity in mAh
+ */
+static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample)
+{
+ int i;
+ struct timespec ts;
+ struct ab8500_fg_avg_cap *avg = &di->avg_cap;
+
+ getnstimeofday(&ts);
+
+ for (i = 0; i < NBR_AVG_SAMPLES; i++) {
+ avg->samples[i] = sample;
+ avg->time_stamps[i] = ts.tv_sec;
+ }
+
+ avg->pos = 0;
+ avg->nbr_samples = NBR_AVG_SAMPLES;
+ avg->sum = sample * NBR_AVG_SAMPLES;
+ avg->avg = sample;
+}
+
+/**
+ * ab8500_fg_coulomb_counter() - enable coulomb counter
+ * @di: pointer to the ab8500_fg structure
+ * @enable: enable/disable
+ *
+ * Enable/Disable coulomb counter.
+ * On failure returns negative value.
+ */
+static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
+{
+ int ret = 0;
+ mutex_lock(&di->cc_lock);
+ if (enable) {
+ /* To be able to reprogram the number of samples, we have to
+ * first stop the CC and then enable it again */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+ AB8500_RTC_CC_CONF_REG, 0x00);
+ if (ret)
+ goto cc_err;
+
+ /* Program the samples */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
+ di->fg_samples);
+ if (ret)
+ goto cc_err;
+
+ /* Start the CC */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+ AB8500_RTC_CC_CONF_REG,
+ (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
+ if (ret)
+ goto cc_err;
+
+ di->flags.fg_enabled = true;
+ } else {
+ /* Clear any pending read requests */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
+ if (ret)
+ goto cc_err;
+
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU_CTRL, 0);
+ if (ret)
+ goto cc_err;
+
+ /* Stop the CC */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+ AB8500_RTC_CC_CONF_REG, 0);
+ if (ret)
+ goto cc_err;
+
+ di->flags.fg_enabled = false;
+
+ }
+ dev_dbg(di->dev, " CC enabled: %d Samples: %d\n",
+ enable, di->fg_samples);
+
+ mutex_unlock(&di->cc_lock);
+
+ return ret;
+cc_err:
+ dev_err(di->dev, "%s Enabling coulomb counter failed\n", __func__);
+ mutex_unlock(&di->cc_lock);
+ return ret;
+}
+
+/**
+ * ab8500_fg_inst_curr_start() - start battery instantaneous current
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Returns 0 or error code
+ * Note: This is part "one" and has to be called before
+ * ab8500_fg_inst_curr_finalize()
+ */
+ int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
+{
+ u8 reg_val;
+ int ret;
+
+ mutex_lock(&di->cc_lock);
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+ AB8500_RTC_CC_CONF_REG, &reg_val);
+ if (ret < 0)
+ goto fail;
+
+ if (!(reg_val & CC_PWR_UP_ENA)) {
+ dev_dbg(di->dev, "%s Enable FG\n", __func__);
+ di->turn_off_fg = true;
+
+ /* Program the samples */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
+ SEC_TO_SAMPLE(10));
+ if (ret)
+ goto fail;
+
+ /* Start the CC */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+ AB8500_RTC_CC_CONF_REG,
+ (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
+ if (ret)
+ goto fail;
+ } else {
+ di->turn_off_fg = false;
+ }
+
+ /* Return and WFI */
+ INIT_COMPLETION(di->ab8500_fg_complete);
+ enable_irq(di->irq);
+
+ /* Note: cc_lock is still locked */
+ return 0;
+fail:
+ mutex_unlock(&di->cc_lock);
+ return ret;
+}
+
+/**
+ * ab8500_fg_inst_curr_done() - check if fg conversion is done
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Returns 1 if conversion done, 0 if still waiting
+ */
+int ab8500_fg_inst_curr_done(struct ab8500_fg *di)
+{
+ return completion_done(&di->ab8500_fg_complete);
+}
+
+/**
+ * ab8500_fg_inst_curr_finalize() - battery instantaneous current
+ * @di: pointer to the ab8500_fg structure
+ * @res: battery instantenous current(on success)
+ *
+ * Returns 0 or an error code
+ * Note: This is part "two" and has to be called at earliest 250 ms
+ * after ab8500_fg_inst_curr_start()
+ */
+int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
+{
+ u8 low, high;
+ int val;
+ int ret;
+ int timeout;
+
+ if (!completion_done(&di->ab8500_fg_complete)) {
+ timeout = wait_for_completion_timeout(&di->ab8500_fg_complete,
+ INS_CURR_TIMEOUT);
+ dev_dbg(di->dev, "Finalize time: %d ms\n",
+ ((INS_CURR_TIMEOUT - timeout) * 1000) / HZ);
+ if (!timeout) {
+ ret = -ETIME;
+ disable_irq(di->irq);
+ dev_err(di->dev, "completion timed out [%d]\n",
+ __LINE__);
+ goto fail;
+ }
+ }
+
+ disable_irq(di->irq);
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ READ_REQ, READ_REQ);
+
+ /* 100uS between read request and read is needed */
+ usleep_range(100, 100);
+
+ /* Read CC Sample conversion value Low and high */
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_SMPL_CNVL_REG, &low);
+ if (ret < 0)
+ goto fail;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_SMPL_CNVH_REG, &high);
+ if (ret < 0)
+ goto fail;
+
+ /*
+ * negative value for Discharging
+ * convert 2's compliment into decimal
+ */
+ if (high & 0x10)
+ val = (low | (high << 8) | 0xFFFFE000);
+ else
+ val = (low | (high << 8));
+
+ /*
+ * Convert to unit value in mA
+ * Full scale input voltage is
+ * 66.660mV => LSB = 66.660mV/(4096*res) = 1.627mA
+ * Given a 250ms conversion cycle time the LSB corresponds
+ * to 112.9 nAh. Convert to current by dividing by the conversion
+ * time in hours (250ms = 1 / (3600 * 4)h)
+ * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
+ */
+ val = (val * QLSB_NANO_AMP_HOURS_X10 * 36 * 4) /
+ (1000 * di->bat->fg_res);
+
+ if (di->turn_off_fg) {
+ dev_dbg(di->dev, "%s Disable FG\n", __func__);
+
+ /* Clear any pending read requests */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
+ if (ret)
+ goto fail;
+
+ /* Stop the CC */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+ AB8500_RTC_CC_CONF_REG, 0);
+ if (ret)
+ goto fail;
+ }
+ mutex_unlock(&di->cc_lock);
+ (*res) = val;
+
+ return 0;
+fail:
+ mutex_unlock(&di->cc_lock);
+ return ret;
+}
+
+/**
+ * ab8500_fg_inst_curr_blocking() - battery instantaneous current
+ * @di: pointer to the ab8500_fg structure
+ * @res: battery instantenous current(on success)
+ *
+ * Returns 0 else error code
+ */
+int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
+{
+ int ret;
+ int res = 0;
+
+ ret = ab8500_fg_inst_curr_start(di);
+ if (ret) {
+ dev_err(di->dev, "Failed to initialize fg_inst\n");
+ return 0;
+ }
+
+ ret = ab8500_fg_inst_curr_finalize(di, &res);
+ if (ret) {
+ dev_err(di->dev, "Failed to finalize fg_inst\n");
+ return 0;
+ }
+
+ return res;
+}
+
+/**
+ * ab8500_fg_acc_cur_work() - average battery current
+ * @work: pointer to the work_struct structure
+ *
+ * Updated the average battery current obtained from the
+ * coulomb counter.
+ */
+static void ab8500_fg_acc_cur_work(struct work_struct *work)
+{
+ int val;
+ int ret;
+ u8 low, med, high;
+
+ struct ab8500_fg *di = container_of(work,
+ struct ab8500_fg, fg_acc_cur_work);
+
+ mutex_lock(&di->cc_lock);
+ ret = abx500_set_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU_CTRL, RD_NCONV_ACCU_REQ);
+ if (ret)
+ goto exit;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU_LOW, &low);
+ if (ret < 0)
+ goto exit;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU_MED, &med);
+ if (ret < 0)
+ goto exit;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU_HIGH, &high);
+ if (ret < 0)
+ goto exit;
+
+ /* Check for sign bit in case of negative value, 2's compliment */
+ if (high & 0x10)
+ val = (low | (med << 8) | (high << 16) | 0xFFE00000);
+ else
+ val = (low | (med << 8) | (high << 16));
+
+ /*
+ * Convert to uAh
+ * Given a 250ms conversion cycle time the LSB corresponds
+ * to 112.9 nAh.
+ * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
+ */
+ di->accu_charge = (val * QLSB_NANO_AMP_HOURS_X10) /
+ (100 * di->bat->fg_res);
+
+ /*
+ * Convert to unit value in mA
+ * Full scale input voltage is
+ * 66.660mV => LSB = 66.660mV/(4096*res) = 1.627mA
+ * Given a 250ms conversion cycle time the LSB corresponds
+ * to 112.9 nAh. Convert to current by dividing by the conversion
+ * time in hours (= samples / (3600 * 4)h)
+ * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
+ */
+ di->avg_curr = (val * QLSB_NANO_AMP_HOURS_X10 * 36) /
+ (1000 * di->bat->fg_res * (di->fg_samples / 4));
+
+ di->flags.conv_done = true;
+
+ mutex_unlock(&di->cc_lock);
+
+ queue_work(di->fg_wq, &di->fg_work);
+
+ return;
+exit:
+ dev_err(di->dev,
+ "Failed to read or write gas gauge registers\n");
+ mutex_unlock(&di->cc_lock);
+ queue_work(di->fg_wq, &di->fg_work);
+}
+
+/**
+ * ab8500_fg_bat_voltage() - get battery voltage
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Returns battery voltage(on success) else error code
+ */
+static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
+{
+ int vbat;
+ static int prev;
+
+ vbat = ab8500_gpadc_convert(di->gpadc, MAIN_BAT_V);
+ if (vbat < 0) {
+ dev_err(di->dev,
+ "%s gpadc conversion failed, using previous value\n",
+ __func__);
+ return prev;
+ }
+
+ prev = vbat;
+ return vbat;
+}
+
+/**
+ * ab8500_fg_volt_to_capacity() - Voltage based capacity
+ * @di: pointer to the ab8500_fg structure
+ * @voltage: The voltage to convert to a capacity
+ *
+ * Returns battery capacity in per mille based on voltage
+ */
+static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
+{
+ int i, tbl_size;
+ struct abx500_v_to_cap *tbl;
+ int cap = 0;
+
+ tbl = di->bat->bat_type[di->bat->batt_id].v_to_cap_tbl,
+ tbl_size = di->bat->bat_type[di->bat->batt_id].n_v_cap_tbl_elements;
+
+ for (i = 0; i < tbl_size; ++i) {
+ if (voltage > tbl[i].voltage)
+ break;
+ }
+
+ if ((i > 0) && (i < tbl_size)) {
+ cap = interpolate(voltage,
+ tbl[i].voltage,
+ tbl[i].capacity * 10,
+ tbl[i-1].voltage,
+ tbl[i-1].capacity * 10);
+ } else if (i == 0) {
+ cap = 1000;
+ } else {
+ cap = 0;
+ }
+
+ dev_dbg(di->dev, "%s Vbat: %d, Cap: %d per mille",
+ __func__, voltage, cap);
+
+ return cap;
+}
+
+/**
+ * ab8500_fg_uncomp_volt_to_capacity() - Uncompensated voltage based capacity
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Returns battery capacity based on battery voltage that is not compensated
+ * for the voltage drop due to the load
+ */
+static int ab8500_fg_uncomp_volt_to_capacity(struct ab8500_fg *di)
+{
+ di->vbat = ab8500_fg_bat_voltage(di);
+ return ab8500_fg_volt_to_capacity(di, di->vbat);
+}
+
+/**
+ * ab8500_fg_battery_resistance() - Returns the battery inner resistance
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Returns battery inner resistance added with the fuel gauge resistor value
+ * to get the total resistance in the whole link from gnd to bat+ node.
+ */
+static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
+{
+ int i, tbl_size;
+ struct batres_vs_temp *tbl;
+ int resist = 0;
+
+ tbl = di->bat->bat_type[di->bat->batt_id].batres_tbl;
+ tbl_size = di->bat->bat_type[di->bat->batt_id].n_batres_tbl_elements;
+
+ for (i = 0; i < tbl_size; ++i) {
+ if (di->bat_temp / 10 > tbl[i].temp)
+ break;
+ }
+
+ if ((i > 0) && (i < tbl_size)) {
+ resist = interpolate(di->bat_temp / 10,
+ tbl[i].temp,
+ tbl[i].resist,
+ tbl[i-1].temp,
+ tbl[i-1].resist);
+ } else if (i == 0) {
+ resist = tbl[0].resist;
+ } else {
+ resist = tbl[tbl_size - 1].resist;
+ }
+
+ dev_dbg(di->dev, "%s Temp: %d battery internal resistance: %d"
+ " fg resistance %d, total: %d (mOhm)\n",
+ __func__, di->bat_temp, resist, di->bat->fg_res / 10,
+ (di->bat->fg_res / 10) + resist);
+
+ /* fg_res variable is in 0.1mOhm */
+ resist += di->bat->fg_res / 10;
+
+ return resist;
+}
+
+/**
+ * ab8500_fg_load_comp_volt_to_capacity() - Load compensated voltage based capacity
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Returns battery capacity based on battery voltage that is load compensated
+ * for the voltage drop
+ */
+static int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di)
+{
+ int vbat_comp, res;
+ int i = 0;
+ int vbat = 0;
+
+ ab8500_fg_inst_curr_start(di);
+
+ do {
+ vbat += ab8500_fg_bat_voltage(di);
+ i++;
+ msleep(5);
+ } while (!ab8500_fg_inst_curr_done(di));
+
+ ab8500_fg_inst_curr_finalize(di, &di->inst_curr);
+
+ di->vbat = vbat / i;
+ res = ab8500_fg_battery_resistance(di);
+
+ /* Use Ohms law to get the load compensated voltage */
+ vbat_comp = di->vbat - (di->inst_curr * res) / 1000;
+
+ dev_dbg(di->dev, "%s Measured Vbat: %dmV,Compensated Vbat %dmV, "
+ "R: %dmOhm, Current: %dmA Vbat Samples: %d\n",
+ __func__, di->vbat, vbat_comp, res, di->inst_curr, i);
+
+ return ab8500_fg_volt_to_capacity(di, vbat_comp);
+}
+
+/**
+ * ab8500_fg_convert_mah_to_permille() - Capacity in mAh to permille
+ * @di: pointer to the ab8500_fg structure
+ * @cap_mah: capacity in mAh
+ *
+ * Converts capacity in mAh to capacity in permille
+ */
+static int ab8500_fg_convert_mah_to_permille(struct ab8500_fg *di, int cap_mah)
+{
+ return (cap_mah * 1000) / di->bat_cap.max_mah_design;
+}
+
+/**
+ * ab8500_fg_convert_permille_to_mah() - Capacity in permille to mAh
+ * @di: pointer to the ab8500_fg structure
+ * @cap_pm: capacity in permille
+ *
+ * Converts capacity in permille to capacity in mAh
+ */
+static int ab8500_fg_convert_permille_to_mah(struct ab8500_fg *di, int cap_pm)
+{
+ return cap_pm * di->bat_cap.max_mah_design / 1000;
+}
+
+/**
+ * ab8500_fg_convert_mah_to_uwh() - Capacity in mAh to uWh
+ * @di: pointer to the ab8500_fg structure
+ * @cap_mah: capacity in mAh
+ *
+ * Converts capacity in mAh to capacity in uWh
+ */
+static int ab8500_fg_convert_mah_to_uwh(struct ab8500_fg *di, int cap_mah)
+{
+ u64 div_res;
+ u32 div_rem;
+
+ div_res = ((u64) cap_mah) * ((u64) di->vbat_nom);
+ div_rem = do_div(div_res, 1000);
+
+ /* Make sure to round upwards if necessary */
+ if (div_rem >= 1000 / 2)
+ div_res++;
+
+ return (int) div_res;
+}
+
+/**
+ * ab8500_fg_calc_cap_charging() - Calculate remaining capacity while charging
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return the capacity in mAh based on previous calculated capcity and the FG
+ * accumulator register value. The filter is filled with this capacity
+ */
+static int ab8500_fg_calc_cap_charging(struct ab8500_fg *di)
+{
+ dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n",
+ __func__,
+ di->bat_cap.mah,
+ di->accu_charge);
+
+ /* Capacity should not be less than 0 */
+ if (di->bat_cap.mah + di->accu_charge > 0)
+ di->bat_cap.mah += di->accu_charge;
+ else
+ di->bat_cap.mah = 0;
+ /*
+ * We force capacity to 100% once when the algorithm
+ * reports that it's full.
+ */
+ if (di->bat_cap.mah >= di->bat_cap.max_mah_design ||
+ di->flags.force_full) {
+ di->bat_cap.mah = di->bat_cap.max_mah_design;
+ }
+
+ ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
+ di->bat_cap.permille =
+ ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+
+ /* We need to update battery voltage and inst current when charging */
+ di->vbat = ab8500_fg_bat_voltage(di);
+ di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+
+ return di->bat_cap.mah;
+}
+
+/**
+ * ab8500_fg_calc_cap_discharge_voltage() - Capacity in discharge with voltage
+ * @di: pointer to the ab8500_fg structure
+ * @comp: if voltage should be load compensated before capacity calc
+ *
+ * Return the capacity in mAh based on the battery voltage. The voltage can
+ * either be load compensated or not. This value is added to the filter and a
+ * new mean value is calculated and returned.
+ */
+static int ab8500_fg_calc_cap_discharge_voltage(struct ab8500_fg *di, bool comp)
+{
+ int permille, mah;
+
+ if (comp)
+ permille = ab8500_fg_load_comp_volt_to_capacity(di);
+ else
+ permille = ab8500_fg_uncomp_volt_to_capacity(di);
+
+ mah = ab8500_fg_convert_permille_to_mah(di, permille);
+
+ di->bat_cap.mah = ab8500_fg_add_cap_sample(di, mah);
+ di->bat_cap.permille =
+ ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+
+ return di->bat_cap.mah;
+}
+
+/**
+ * ab8500_fg_calc_cap_discharge_fg() - Capacity in discharge with FG
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return the capacity in mAh based on previous calculated capcity and the FG
+ * accumulator register value. This value is added to the filter and a
+ * new mean value is calculated and returned.
+ */
+static int ab8500_fg_calc_cap_discharge_fg(struct ab8500_fg *di)
+{
+ int permille_volt, permille;
+
+ dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n",
+ __func__,
+ di->bat_cap.mah,
+ di->accu_charge);
+
+ /* Capacity should not be less than 0 */
+ if (di->bat_cap.mah + di->accu_charge > 0)
+ di->bat_cap.mah += di->accu_charge;
+ else
+ di->bat_cap.mah = 0;
+
+ if (di->bat_cap.mah >= di->bat_cap.max_mah_design)
+ di->bat_cap.mah = di->bat_cap.max_mah_design;
+
+ /*
+ * Check against voltage based capacity. It can not be lower
+ * than what the uncompensated voltage says
+ */
+ permille = ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+ permille_volt = ab8500_fg_uncomp_volt_to_capacity(di);
+
+ if (permille < permille_volt) {
+ di->bat_cap.permille = permille_volt;
+ di->bat_cap.mah = ab8500_fg_convert_permille_to_mah(di,
+ di->bat_cap.permille);
+
+ dev_dbg(di->dev, "%s voltage based: perm %d perm_volt %d\n",
+ __func__,
+ permille,
+ permille_volt);
+
+ ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
+ } else {
+ ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
+ di->bat_cap.permille =
+ ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+ }
+
+ return di->bat_cap.mah;
+}
+
+/**
+ * ab8500_fg_capacity_level() - Get the battery capacity level
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Get the battery capacity level based on the capacity in percent
+ */
+static int ab8500_fg_capacity_level(struct ab8500_fg *di)
+{
+ int ret, percent;
+
+ percent = di->bat_cap.permille / 10;
+
+ if (percent <= di->bat->cap_levels->critical ||
+ di->flags.low_bat)
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+ else if (percent <= di->bat->cap_levels->low)
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+ else if (percent <= di->bat->cap_levels->normal)
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+ else if (percent <= di->bat->cap_levels->high)
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
+ else
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+
+ return ret;
+}
+
+/**
+ * ab8500_fg_check_capacity_limits() - Check if capacity has changed
+ * @di: pointer to the ab8500_fg structure
+ * @init: capacity is allowed to go up in init mode
+ *
+ * Check if capacity or capacity limit has changed and notify the system
+ * about it using the power_supply framework
+ */
+static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
+{
+ bool changed = false;
+
+ di->bat_cap.level = ab8500_fg_capacity_level(di);
+
+ if (di->bat_cap.level != di->bat_cap.prev_level) {
+ /*
+ * We do not allow reported capacity level to go up
+ * unless we're charging or if we're in init
+ */
+ if (!(!di->flags.charging && di->bat_cap.level >
+ di->bat_cap.prev_level) || init) {
+ dev_dbg(di->dev, "level changed from %d to %d\n",
+ di->bat_cap.prev_level,
+ di->bat_cap.level);
+ di->bat_cap.prev_level = di->bat_cap.level;
+ changed = true;
+ } else {
+ dev_dbg(di->dev, "level not allowed to go up "
+ "since no charger is connected: %d to %d\n",
+ di->bat_cap.prev_level,
+ di->bat_cap.level);
+ }
+ }
+
+ /*
+ * If we have received the LOW_BAT IRQ, set capacity to 0 to initiate
+ * shutdown
+ */
+ if (di->flags.low_bat) {
+ dev_dbg(di->dev, "Battery low, set capacity to 0\n");
+ di->bat_cap.prev_percent = 0;
+ di->bat_cap.permille = 0;
+ di->bat_cap.prev_mah = 0;
+ di->bat_cap.mah = 0;
+ changed = true;
+ } else if (di->flags.fully_charged) {
+ /*
+ * We report 100% if algorithm reported fully charged
+ * unless capacity drops too much
+ */
+ if (di->flags.force_full) {
+ di->bat_cap.prev_percent = di->bat_cap.permille / 10;
+ di->bat_cap.prev_mah = di->bat_cap.mah;
+ } else if (!di->flags.force_full &&
+ di->bat_cap.prev_percent !=
+ (di->bat_cap.permille) / 10 &&
+ (di->bat_cap.permille / 10) <
+ di->bat->fg_params->maint_thres) {
+ dev_dbg(di->dev,
+ "battery reported full "
+ "but capacity dropping: %d\n",
+ di->bat_cap.permille / 10);
+ di->bat_cap.prev_percent = di->bat_cap.permille / 10;
+ di->bat_cap.prev_mah = di->bat_cap.mah;
+
+ changed = true;
+ }
+ } else if (di->bat_cap.prev_percent != di->bat_cap.permille / 10) {
+ if (di->bat_cap.permille / 10 == 0) {
+ /*
+ * We will not report 0% unless we've got
+ * the LOW_BAT IRQ, no matter what the FG
+ * algorithm says.
+ */
+ di->bat_cap.prev_percent = 1;
+ di->bat_cap.permille = 1;
+ di->bat_cap.prev_mah = 1;
+ di->bat_cap.mah = 1;
+
+ changed = true;
+ } else if (!(!di->flags.charging &&
+ (di->bat_cap.permille / 10) >
+ di->bat_cap.prev_percent) || init) {
+ /*
+ * We do not allow reported capacity to go up
+ * unless we're charging or if we're in init
+ */
+ dev_dbg(di->dev,
+ "capacity changed from %d to %d (%d)\n",
+ di->bat_cap.prev_percent,
+ di->bat_cap.permille / 10,
+ di->bat_cap.permille);
+ di->bat_cap.prev_percent = di->bat_cap.permille / 10;
+ di->bat_cap.prev_mah = di->bat_cap.mah;
+
+ changed = true;
+ } else {
+ dev_dbg(di->dev, "capacity not allowed to go up since "
+ "no charger is connected: %d to %d (%d)\n",
+ di->bat_cap.prev_percent,
+ di->bat_cap.permille / 10,
+ di->bat_cap.permille);
+ }
+ }
+
+ if (changed) {
+ power_supply_changed(&di->fg_psy);
+ if (di->flags.fully_charged && di->flags.force_full) {
+ dev_dbg(di->dev, "Battery full, notifying.\n");
+ di->flags.force_full = false;
+ sysfs_notify(&di->fg_kobject, NULL, "charge_full");
+ }
+ sysfs_notify(&di->fg_kobject, NULL, "charge_now");
+ }
+}
+
+static void ab8500_fg_charge_state_to(struct ab8500_fg *di,
+ enum ab8500_fg_charge_state new_state)
+{
+ dev_dbg(di->dev, "Charge state from %d [%s] to %d [%s]\n",
+ di->charge_state,
+ charge_state[di->charge_state],
+ new_state,
+ charge_state[new_state]);
+
+ di->charge_state = new_state;
+}
+
+static void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
+ enum ab8500_fg_discharge_state new_state)
+{
+ dev_dbg(di->dev, "Disharge state from %d [%s] to %d [%s]\n",
+ di->discharge_state,
+ discharge_state[di->discharge_state],
+ new_state,
+ discharge_state[new_state]);
+
+ di->discharge_state = new_state;
+}
+
+/**
+ * ab8500_fg_algorithm_charging() - FG algorithm for when charging
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Battery capacity calculation state machine for when we're charging
+ */
+static void ab8500_fg_algorithm_charging(struct ab8500_fg *di)
+{
+ /*
+ * If we change to discharge mode
+ * we should start with recovery
+ */
+ if (di->discharge_state != AB8500_FG_DISCHARGE_INIT_RECOVERY)
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_INIT_RECOVERY);
+
+ switch (di->charge_state) {
+ case AB8500_FG_CHARGE_INIT:
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_charging);
+
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_READOUT);
+
+ break;
+
+ case AB8500_FG_CHARGE_READOUT:
+ /*
+ * Read the FG and calculate the new capacity
+ */
+ mutex_lock(&di->cc_lock);
+ if (!di->flags.conv_done) {
+ /* Wasn't the CC IRQ that got us here */
+ mutex_unlock(&di->cc_lock);
+ dev_dbg(di->dev, "%s CC conv not done\n",
+ __func__);
+
+ break;
+ }
+ di->flags.conv_done = false;
+ mutex_unlock(&di->cc_lock);
+
+ ab8500_fg_calc_cap_charging(di);
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* Check capacity limits */
+ ab8500_fg_check_capacity_limits(di, false);
+}
+
+static void force_capacity(struct ab8500_fg *di)
+{
+ int cap;
+
+ ab8500_fg_clear_cap_samples(di);
+ cap = di->bat_cap.user_mah;
+ if (cap > di->bat_cap.max_mah_design) {
+ dev_dbg(di->dev, "Remaining cap %d can't be bigger than total"
+ " %d\n", cap, di->bat_cap.max_mah_design);
+ cap = di->bat_cap.max_mah_design;
+ }
+ ab8500_fg_fill_cap_sample(di, di->bat_cap.user_mah);
+ di->bat_cap.permille = ab8500_fg_convert_mah_to_permille(di, cap);
+ di->bat_cap.mah = cap;
+ ab8500_fg_check_capacity_limits(di, true);
+}
+
+static bool check_sysfs_capacity(struct ab8500_fg *di)
+{
+ int cap, lower, upper;
+ int cap_permille;
+
+ cap = di->bat_cap.user_mah;
+
+ cap_permille = ab8500_fg_convert_mah_to_permille(di,
+ di->bat_cap.user_mah);
+
+ lower = di->bat_cap.permille - di->bat->fg_params->user_cap_limit * 10;
+ upper = di->bat_cap.permille + di->bat->fg_params->user_cap_limit * 10;
+
+ if (lower < 0)
+ lower = 0;
+ /* 1000 is permille, -> 100 percent */
+ if (upper > 1000)
+ upper = 1000;
+
+ dev_dbg(di->dev, "Capacity limits:"
+ " (Lower: %d User: %d Upper: %d) [user: %d, was: %d]\n",
+ lower, cap_permille, upper, cap, di->bat_cap.mah);
+
+ /* If within limits, use the saved capacity and exit estimation...*/
+ if (cap_permille > lower && cap_permille < upper) {
+ dev_dbg(di->dev, "OK! Using users cap %d uAh now\n", cap);
+ force_capacity(di);
+ return true;
+ }
+ dev_dbg(di->dev, "Capacity from user out of limits, ignoring");
+ return false;
+}
+
+/**
+ * ab8500_fg_algorithm_discharging() - FG algorithm for when discharging
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Battery capacity calculation state machine for when we're discharging
+ */
+static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
+{
+ int sleep_time;
+
+ /* If we change to charge mode we should start with init */
+ if (di->charge_state != AB8500_FG_CHARGE_INIT)
+ ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+
+ switch (di->discharge_state) {
+ case AB8500_FG_DISCHARGE_INIT:
+ /* We use the FG IRQ to work on */
+ di->init_cnt = 0;
+ di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer);
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_INITMEASURING);
+
+ /* Intentional fallthrough */
+ case AB8500_FG_DISCHARGE_INITMEASURING:
+ /*
+ * Discard a number of samples during startup.
+ * After that, use compensated voltage for a few
+ * samples to get an initial capacity.
+ * Then go to READOUT
+ */
+ sleep_time = di->bat->fg_params->init_timer;
+
+ /* Discard the first [x] seconds */
+ if (di->init_cnt >
+ di->bat->fg_params->init_discard_time) {
+ ab8500_fg_calc_cap_discharge_voltage(di, true);
+
+ ab8500_fg_check_capacity_limits(di, true);
+ }
+
+ di->init_cnt += sleep_time;
+ if (di->init_cnt > di->bat->fg_params->init_total_time)
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_READOUT_INIT);
+
+ break;
+
+ case AB8500_FG_DISCHARGE_INIT_RECOVERY:
+ di->recovery_cnt = 0;
+ di->recovery_needed = true;
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_RECOVERY);
+
+ /* Intentional fallthrough */
+
+ case AB8500_FG_DISCHARGE_RECOVERY:
+ sleep_time = di->bat->fg_params->recovery_sleep_timer;
+
+ /*
+ * We should check the power consumption
+ * If low, go to READOUT (after x min) or
+ * RECOVERY_SLEEP if time left.
+ * If high, go to READOUT
+ */
+ di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+
+ if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
+ if (di->recovery_cnt >
+ di->bat->fg_params->recovery_total_time) {
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_READOUT);
+ di->recovery_needed = false;
+ } else {
+ queue_delayed_work(di->fg_wq,
+ &di->fg_periodic_work,
+ sleep_time * HZ);
+ }
+ di->recovery_cnt += sleep_time;
+ } else {
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_READOUT);
+ }
+ break;
+
+ case AB8500_FG_DISCHARGE_READOUT_INIT:
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_READOUT);
+ break;
+
+ case AB8500_FG_DISCHARGE_READOUT:
+ di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+
+ if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
+ /* Detect mode change */
+ if (di->high_curr_mode) {
+ di->high_curr_mode = false;
+ di->high_curr_cnt = 0;
+ }
+
+ if (di->recovery_needed) {
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_RECOVERY);
+
+ queue_delayed_work(di->fg_wq,
+ &di->fg_periodic_work, 0);
+
+ break;
+ }
+
+ ab8500_fg_calc_cap_discharge_voltage(di, true);
+ } else {
+ mutex_lock(&di->cc_lock);
+ if (!di->flags.conv_done) {
+ /* Wasn't the CC IRQ that got us here */
+ mutex_unlock(&di->cc_lock);
+ dev_dbg(di->dev, "%s CC conv not done\n",
+ __func__);
+
+ break;
+ }
+ di->flags.conv_done = false;
+ mutex_unlock(&di->cc_lock);
+
+ /* Detect mode change */
+ if (!di->high_curr_mode) {
+ di->high_curr_mode = true;
+ di->high_curr_cnt = 0;
+ }
+
+ di->high_curr_cnt +=
+ di->bat->fg_params->accu_high_curr;
+ if (di->high_curr_cnt >
+ di->bat->fg_params->high_curr_time)
+ di->recovery_needed = true;
+
+ ab8500_fg_calc_cap_discharge_fg(di);
+ }
+
+ ab8500_fg_check_capacity_limits(di, false);
+
+ break;
+
+ case AB8500_FG_DISCHARGE_WAKEUP:
+ ab8500_fg_coulomb_counter(di, true);
+ di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+
+ ab8500_fg_calc_cap_discharge_voltage(di, true);
+
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+ ab8500_fg_coulomb_counter(di, true);
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_READOUT);
+
+ ab8500_fg_check_capacity_limits(di, false);
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * ab8500_fg_algorithm_calibrate() - Internal columb counter offset calibration
+ * @di: pointer to the ab8500_fg structure
+ *
+ */
+static void ab8500_fg_algorithm_calibrate(struct ab8500_fg *di)
+{
+ int ret;
+
+ switch (di->calib_state) {
+ case AB8500_FG_CALIB_INIT:
+ dev_dbg(di->dev, "Calibration ongoing...\n");
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_INT_CAL_N_AVG_MASK, CC_INT_CAL_SAMPLES_8);
+ if (ret < 0)
+ goto err;
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_INTAVGOFFSET_ENA, CC_INTAVGOFFSET_ENA);
+ if (ret < 0)
+ goto err;
+ di->calib_state = AB8500_FG_CALIB_WAIT;
+ break;
+ case AB8500_FG_CALIB_END:
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_MUXOFFSET, CC_MUXOFFSET);
+ if (ret < 0)
+ goto err;
+ di->flags.calibrate = false;
+ dev_dbg(di->dev, "Calibration done...\n");
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ break;
+ case AB8500_FG_CALIB_WAIT:
+ dev_dbg(di->dev, "Calibration WFI\n");
+ default:
+ break;
+ }
+ return;
+err:
+ /* Something went wrong, don't calibrate then */
+ dev_err(di->dev, "failed to calibrate the CC\n");
+ di->flags.calibrate = false;
+ di->calib_state = AB8500_FG_CALIB_INIT;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+}
+
+/**
+ * ab8500_fg_algorithm() - Entry point for the FG algorithm
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Entry point for the battery capacity calculation state machine
+ */
+static void ab8500_fg_algorithm(struct ab8500_fg *di)
+{
+ if (di->flags.calibrate)
+ ab8500_fg_algorithm_calibrate(di);
+ else {
+ if (di->flags.charging)
+ ab8500_fg_algorithm_charging(di);
+ else
+ ab8500_fg_algorithm_discharging(di);
+ }
+
+ dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d\n",
+ di->bat_cap.max_mah_design,
+ di->bat_cap.mah,
+ di->bat_cap.permille,
+ di->bat_cap.level,
+ di->bat_cap.prev_mah,
+ di->bat_cap.prev_percent,
+ di->bat_cap.prev_level,
+ di->vbat,
+ di->inst_curr,
+ di->avg_curr,
+ di->accu_charge,
+ di->flags.charging,
+ di->charge_state,
+ di->discharge_state,
+ di->high_curr_mode,
+ di->recovery_needed);
+}
+
+/**
+ * ab8500_fg_periodic_work() - Run the FG state machine periodically
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for periodic work
+ */
+static void ab8500_fg_periodic_work(struct work_struct *work)
+{
+ struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+ fg_periodic_work.work);
+
+ if (di->init_capacity) {
+ /* A dummy read that will return 0 */
+ di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+ /* Get an initial capacity calculation */
+ ab8500_fg_calc_cap_discharge_voltage(di, true);
+ ab8500_fg_check_capacity_limits(di, true);
+ di->init_capacity = false;
+
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ } else if (di->flags.user_cap) {
+ if (check_sysfs_capacity(di)) {
+ ab8500_fg_check_capacity_limits(di, true);
+ if (di->flags.charging)
+ ab8500_fg_charge_state_to(di,
+ AB8500_FG_CHARGE_INIT);
+ else
+ ab8500_fg_discharge_state_to(di,
+ AB8500_FG_DISCHARGE_READOUT_INIT);
+ }
+ di->flags.user_cap = false;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ } else
+ ab8500_fg_algorithm(di);
+
+}
+
+/**
+ * ab8500_fg_check_hw_failure_work() - Check OVV_BAT condition
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the OVV_BAT condition
+ */
+static void ab8500_fg_check_hw_failure_work(struct work_struct *work)
+{
+ int ret;
+ u8 reg_value;
+
+ struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+ fg_check_hw_failure_work.work);
+
+ /*
+ * If we have had a battery over-voltage situation,
+ * check ovv-bit to see if it should be reset.
+ */
+ if (di->flags.bat_ovv) {
+ ret = abx500_get_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CH_STAT_REG,
+ &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+ return;
+ }
+ if ((reg_value & BATT_OVV) != BATT_OVV) {
+ dev_dbg(di->dev, "Battery recovered from OVV\n");
+ di->flags.bat_ovv = false;
+ power_supply_changed(&di->fg_psy);
+ return;
+ }
+
+ /* Not yet recovered from ovv, reschedule this test */
+ queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work,
+ round_jiffies(HZ));
+ }
+}
+
+/**
+ * ab8500_fg_low_bat_work() - Check LOW_BAT condition
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the LOW_BAT condition
+ */
+static void ab8500_fg_low_bat_work(struct work_struct *work)
+{
+ int vbat;
+
+ struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+ fg_low_bat_work.work);
+
+ vbat = ab8500_fg_bat_voltage(di);
+
+ /* Check if LOW_BAT still fulfilled */
+ if (vbat < di->bat->fg_params->lowbat_threshold) {
+ di->flags.low_bat = true;
+ dev_warn(di->dev, "Battery voltage still LOW\n");
+
+ /*
+ * We need to re-schedule this check to be able to detect
+ * if the voltage increases again during charging
+ */
+ queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
+ round_jiffies(LOW_BAT_CHECK_INTERVAL));
+ } else {
+ di->flags.low_bat = false;
+ dev_warn(di->dev, "Battery voltage OK again\n");
+ }
+
+ /* This is needed to dispatch LOW_BAT */
+ ab8500_fg_check_capacity_limits(di, false);
+
+ /* Set this flag to check if LOW_BAT IRQ still occurs */
+ di->flags.low_bat_delay = false;
+}
+
+/**
+ * ab8500_fg_battok_calc - calculate the bit pattern corresponding
+ * to the target voltage.
+ * @di: pointer to the ab8500_fg structure
+ * @target target voltage
+ *
+ * Returns bit pattern closest to the target voltage
+ * valid return values are 0-14. (0-BATT_OK_MAX_NR_INCREMENTS)
+ */
+
+static int ab8500_fg_battok_calc(struct ab8500_fg *di, int target)
+{
+ if (target > BATT_OK_MIN +
+ (BATT_OK_INCREMENT * BATT_OK_MAX_NR_INCREMENTS))
+ return BATT_OK_MAX_NR_INCREMENTS;
+ if (target < BATT_OK_MIN)
+ return 0;
+ return (target - BATT_OK_MIN) / BATT_OK_INCREMENT;
+}
+
+/**
+ * ab8500_fg_battok_init_hw_register - init battok levels
+ * @di: pointer to the ab8500_fg structure
+ *
+ */
+
+static int ab8500_fg_battok_init_hw_register(struct ab8500_fg *di)
+{
+ int selected;
+ int sel0;
+ int sel1;
+ int cbp_sel0;
+ int cbp_sel1;
+ int ret;
+ int new_val;
+
+ sel0 = di->bat->fg_params->battok_falling_th_sel0;
+ sel1 = di->bat->fg_params->battok_raising_th_sel1;
+
+ cbp_sel0 = ab8500_fg_battok_calc(di, sel0);
+ cbp_sel1 = ab8500_fg_battok_calc(di, sel1);
+
+ selected = BATT_OK_MIN + cbp_sel0 * BATT_OK_INCREMENT;
+
+ if (selected != sel0)
+ dev_warn(di->dev, "Invalid voltage step:%d, using %d %d\n",
+ sel0, selected, cbp_sel0);
+
+ selected = BATT_OK_MIN + cbp_sel1 * BATT_OK_INCREMENT;
+
+ if (selected != sel1)
+ dev_warn(di->dev, "Invalid voltage step:%d, using %d %d\n",
+ sel1, selected, cbp_sel1);
+
+ new_val = cbp_sel0 | (cbp_sel1 << 4);
+
+ dev_dbg(di->dev, "using: %x %d %d\n", new_val, cbp_sel0, cbp_sel1);
+ ret = abx500_set_register_interruptible(di->dev, AB8500_SYS_CTRL2_BLOCK,
+ AB8500_BATT_OK_REG, new_val);
+ return ret;
+}
+
+/**
+ * ab8500_fg_instant_work() - Run the FG state machine instantly
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for instant work
+ */
+static void ab8500_fg_instant_work(struct work_struct *work)
+{
+ struct ab8500_fg *di = container_of(work, struct ab8500_fg, fg_work);
+
+ ab8500_fg_algorithm(di);
+}
+
+/**
+ * ab8500_fg_cc_data_end_handler() - isr to get battery avg current.
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di)
+{
+ struct ab8500_fg *di = _di;
+ complete(&di->ab8500_fg_complete);
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_cc_convend_handler() - isr to get battery avg current.
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_cc_int_calib_handler(int irq, void *_di)
+{
+ struct ab8500_fg *di = _di;
+ di->calib_state = AB8500_FG_CALIB_END;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_cc_convend_handler() - isr to get battery avg current.
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_cc_convend_handler(int irq, void *_di)
+{
+ struct ab8500_fg *di = _di;
+
+ queue_work(di->fg_wq, &di->fg_acc_cur_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_batt_ovv_handler() - Battery OVV occured
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_batt_ovv_handler(int irq, void *_di)
+{
+ struct ab8500_fg *di = _di;
+
+ dev_dbg(di->dev, "Battery OVV\n");
+ di->flags.bat_ovv = true;
+ power_supply_changed(&di->fg_psy);
+
+ /* Schedule a new HW failure check */
+ queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_lowbatf_handler() - Battery voltage is below LOW threshold
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_lowbatf_handler(int irq, void *_di)
+{
+ struct ab8500_fg *di = _di;
+
+ if (!di->flags.low_bat_delay) {
+ dev_warn(di->dev, "Battery voltage is below LOW threshold\n");
+ di->flags.low_bat_delay = true;
+ /*
+ * Start a timer to check LOW_BAT again after some time
+ * This is done to avoid shutdown on single voltage dips
+ */
+ queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
+ round_jiffies(LOW_BAT_CHECK_INTERVAL));
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_get_property() - get the fg properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the
+ * fg properties by reading the sysfs files.
+ * voltage_now: battery voltage
+ * current_now: battery instant current
+ * current_avg: battery average current
+ * charge_full_design: capacity where battery is considered full
+ * charge_now: battery capacity in nAh
+ * capacity: capacity in percent
+ * capacity_level: capacity level
+ *
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_fg_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ab8500_fg *di;
+
+ di = to_ab8500_fg_device_info(psy);
+
+ /*
+ * If battery is identified as unknown and charging of unknown
+ * batteries is disabled, we always report 100% capacity and
+ * capacity level UNKNOWN, since we can't calculate
+ * remaining capacity
+ */
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (di->flags.bat_ovv)
+ val->intval = BATT_OVV_VALUE * 1000;
+ else
+ val->intval = di->vbat * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = di->inst_curr * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = di->avg_curr * 1000;
+ break;
+ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+ val->intval = ab8500_fg_convert_mah_to_uwh(di,
+ di->bat_cap.max_mah_design);
+ break;
+ case POWER_SUPPLY_PROP_ENERGY_FULL:
+ val->intval = ab8500_fg_convert_mah_to_uwh(di,
+ di->bat_cap.max_mah);
+ break;
+ case POWER_SUPPLY_PROP_ENERGY_NOW:
+ if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
+ di->flags.batt_id_received)
+ val->intval = ab8500_fg_convert_mah_to_uwh(di,
+ di->bat_cap.max_mah);
+ else
+ val->intval = ab8500_fg_convert_mah_to_uwh(di,
+ di->bat_cap.prev_mah);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = di->bat_cap.max_mah_design;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = di->bat_cap.max_mah;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
+ di->flags.batt_id_received)
+ val->intval = di->bat_cap.max_mah;
+ else
+ val->intval = di->bat_cap.prev_mah;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
+ di->flags.batt_id_received)
+ val->intval = 100;
+ else
+ val->intval = di->bat_cap.prev_percent;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+ if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
+ di->flags.batt_id_received)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+ else
+ val->intval = di->bat_cap.prev_level;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
+{
+ struct power_supply *psy;
+ struct power_supply *ext;
+ struct ab8500_fg *di;
+ union power_supply_propval ret;
+ int i, j;
+ bool psy_found = false;
+
+ psy = (struct power_supply *)data;
+ ext = dev_get_drvdata(dev);
+ di = to_ab8500_fg_device_info(psy);
+
+ /*
+ * For all psy where the name of your driver
+ * appears in any supplied_to
+ */
+ for (i = 0; i < ext->num_supplicants; i++) {
+ if (!strcmp(ext->supplied_to[i], psy->name))
+ psy_found = true;
+ }
+
+ if (!psy_found)
+ return 0;
+
+ /* Go through all properties for the psy */
+ for (j = 0; j < ext->num_properties; j++) {
+ enum power_supply_property prop;
+ prop = ext->properties[j];
+
+ if (ext->get_property(ext, prop, &ret))
+ continue;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_STATUS:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ switch (ret.intval) {
+ case POWER_SUPPLY_STATUS_UNKNOWN:
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ case POWER_SUPPLY_STATUS_NOT_CHARGING:
+ if (!di->flags.charging)
+ break;
+ di->flags.charging = false;
+ di->flags.fully_charged = false;
+ queue_work(di->fg_wq, &di->fg_work);
+ break;
+ case POWER_SUPPLY_STATUS_FULL:
+ if (di->flags.fully_charged)
+ break;
+ di->flags.fully_charged = true;
+ di->flags.force_full = true;
+ /* Save current capacity as maximum */
+ di->bat_cap.max_mah = di->bat_cap.mah;
+ queue_work(di->fg_wq, &di->fg_work);
+ break;
+ case POWER_SUPPLY_STATUS_CHARGING:
+ if (di->flags.charging)
+ break;
+ di->flags.charging = true;
+ di->flags.fully_charged = false;
+ queue_work(di->fg_wq, &di->fg_work);
+ break;
+ };
+ default:
+ break;
+ };
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ if (!di->flags.batt_id_received) {
+ const struct abx500_battery_type *b;
+
+ b = &(di->bat->bat_type[di->bat->batt_id]);
+
+ di->flags.batt_id_received = true;
+
+ di->bat_cap.max_mah_design =
+ MILLI_TO_MICRO *
+ b->charge_full_design;
+
+ di->bat_cap.max_mah =
+ di->bat_cap.max_mah_design;
+
+ di->vbat_nom = b->nominal_voltage;
+ }
+
+ if (ret.intval)
+ di->flags.batt_unknown = false;
+ else
+ di->flags.batt_unknown = true;
+ break;
+ default:
+ break;
+ }
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ if (di->flags.batt_id_received)
+ di->bat_temp = ret.intval;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ab8500_fg_init_hw_registers() - Set up FG related registers
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Set up battery OVV, low battery voltage registers
+ */
+static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
+{
+ int ret;
+
+ /* Set VBAT OVV threshold */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_BATT_OVV,
+ BATT_OVV_TH_4P75,
+ BATT_OVV_TH_4P75);
+ if (ret) {
+ dev_err(di->dev, "failed to set BATT_OVV\n");
+ goto out;
+ }
+
+ /* Enable VBAT OVV detection */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_BATT_OVV,
+ BATT_OVV_ENA,
+ BATT_OVV_ENA);
+ if (ret) {
+ dev_err(di->dev, "failed to enable BATT_OVV\n");
+ goto out;
+ }
+
+ /* Low Battery Voltage */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_SYS_CTRL2_BLOCK,
+ AB8500_LOW_BAT_REG,
+ ab8500_volt_to_regval(
+ di->bat->fg_params->lowbat_threshold) << 1 |
+ LOW_BAT_ENABLE);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ goto out;
+ }
+
+ /* Battery OK threshold */
+ ret = ab8500_fg_battok_init_hw_register(di);
+ if (ret) {
+ dev_err(di->dev, "BattOk init write failed.\n");
+ goto out;
+ }
+out:
+ return ret;
+}
+
+/**
+ * ab8500_fg_external_power_changed() - callback for power supply changes
+ * @psy: pointer to the structure power_supply
+ *
+ * This function is the entry point of the pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in any external power
+ * supply that this driver needs to be notified of.
+ */
+static void ab8500_fg_external_power_changed(struct power_supply *psy)
+{
+ struct ab8500_fg *di = to_ab8500_fg_device_info(psy);
+
+ class_for_each_device(power_supply_class, NULL,
+ &di->fg_psy, ab8500_fg_get_ext_psy_data);
+}
+
+/**
+ * abab8500_fg_reinit_work() - work to reset the FG algorithm
+ * @work: pointer to the work_struct structure
+ *
+ * Used to reset the current battery capacity to be able to
+ * retrigger a new voltage base capacity calculation. For
+ * test and verification purpose.
+ */
+static void ab8500_fg_reinit_work(struct work_struct *work)
+{
+ struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+ fg_reinit_work.work);
+
+ if (di->flags.calibrate == false) {
+ dev_dbg(di->dev, "Resetting FG state machine to init.\n");
+ ab8500_fg_clear_cap_samples(di);
+ ab8500_fg_calc_cap_discharge_voltage(di, true);
+ ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+ ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+
+ } else {
+ dev_err(di->dev, "Residual offset calibration ongoing "
+ "retrying..\n");
+ /* Wait one second until next try*/
+ queue_delayed_work(di->fg_wq, &di->fg_reinit_work,
+ round_jiffies(1));
+ }
+}
+
+/**
+ * ab8500_fg_reinit() - forces FG algorithm to reinitialize with current values
+ *
+ * This function can be used to force the FG algorithm to recalculate a new
+ * voltage based battery capacity.
+ */
+void ab8500_fg_reinit(void)
+{
+ struct ab8500_fg *di = ab8500_fg_get();
+ /* User won't be notified if a null pointer returned. */
+ if (di != NULL)
+ queue_delayed_work(di->fg_wq, &di->fg_reinit_work, 0);
+}
+
+/* Exposure to the sysfs interface */
+
+struct ab8500_fg_sysfs_entry {
+ struct attribute attr;
+ ssize_t (*show)(struct ab8500_fg *, char *);
+ ssize_t (*store)(struct ab8500_fg *, const char *, size_t);
+};
+
+static ssize_t charge_full_show(struct ab8500_fg *di, char *buf)
+{
+ return sprintf(buf, "%d\n", di->bat_cap.max_mah);
+}
+
+static ssize_t charge_full_store(struct ab8500_fg *di, const char *buf,
+ size_t count)
+{
+ unsigned long charge_full;
+ ssize_t ret = -EINVAL;
+
+ ret = strict_strtoul(buf, 10, &charge_full);
+
+ dev_dbg(di->dev, "Ret %zd charge_full %lu", ret, charge_full);
+
+ if (!ret) {
+ di->bat_cap.max_mah = (int) charge_full;
+ ret = count;
+ }
+ return ret;
+}
+
+static ssize_t charge_now_show(struct ab8500_fg *di, char *buf)
+{
+ return sprintf(buf, "%d\n", di->bat_cap.prev_mah);
+}
+
+static ssize_t charge_now_store(struct ab8500_fg *di, const char *buf,
+ size_t count)
+{
+ unsigned long charge_now;
+ ssize_t ret;
+
+ ret = strict_strtoul(buf, 10, &charge_now);
+
+ dev_dbg(di->dev, "Ret %zd charge_now %lu was %d",
+ ret, charge_now, di->bat_cap.prev_mah);
+
+ if (!ret) {
+ di->bat_cap.user_mah = (int) charge_now;
+ di->flags.user_cap = true;
+ ret = count;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ }
+ return ret;
+}
+
+static struct ab8500_fg_sysfs_entry charge_full_attr =
+ __ATTR(charge_full, 0644, charge_full_show, charge_full_store);
+
+static struct ab8500_fg_sysfs_entry charge_now_attr =
+ __ATTR(charge_now, 0644, charge_now_show, charge_now_store);
+
+static ssize_t
+ab8500_fg_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ struct ab8500_fg_sysfs_entry *entry;
+ struct ab8500_fg *di;
+
+ entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr);
+ di = container_of(kobj, struct ab8500_fg, fg_kobject);
+
+ if (!entry->show)
+ return -EIO;
+
+ return entry->show(di, buf);
+}
+static ssize_t
+ab8500_fg_store(struct kobject *kobj, struct attribute *attr, const char *buf,
+ size_t count)
+{
+ struct ab8500_fg_sysfs_entry *entry;
+ struct ab8500_fg *di;
+
+ entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr);
+ di = container_of(kobj, struct ab8500_fg, fg_kobject);
+
+ if (!entry->store)
+ return -EIO;
+
+ return entry->store(di, buf, count);
+}
+
+static const struct sysfs_ops ab8500_fg_sysfs_ops = {
+ .show = ab8500_fg_show,
+ .store = ab8500_fg_store,
+};
+
+static struct attribute *ab8500_fg_attrs[] = {
+ &charge_full_attr.attr,
+ &charge_now_attr.attr,
+ NULL,
+};
+
+static struct kobj_type ab8500_fg_ktype = {
+ .sysfs_ops = &ab8500_fg_sysfs_ops,
+ .default_attrs = ab8500_fg_attrs,
+};
+
+/**
+ * ab8500_chargalg_sysfs_exit() - de-init of sysfs entry
+ * @di: pointer to the struct ab8500_chargalg
+ *
+ * This function removes the entry in sysfs.
+ */
+static void ab8500_fg_sysfs_exit(struct ab8500_fg *di)
+{
+ kobject_del(&di->fg_kobject);
+}
+
+/**
+ * ab8500_chargalg_sysfs_init() - init of sysfs entry
+ * @di: pointer to the struct ab8500_chargalg
+ *
+ * This function adds an entry in sysfs.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_fg_sysfs_init(struct ab8500_fg *di)
+{
+ int ret = 0;
+
+ ret = kobject_init_and_add(&di->fg_kobject,
+ &ab8500_fg_ktype,
+ NULL, "battery");
+ if (ret < 0)
+ dev_err(di->dev, "failed to create sysfs entry\n");
+
+ return ret;
+}
+/* Exposure to the sysfs interface <<END>> */
+
+#if defined(CONFIG_PM)
+static int ab8500_fg_resume(struct platform_device *pdev)
+{
+ struct ab8500_fg *di = platform_get_drvdata(pdev);
+
+ /*
+ * Change state if we're not charging. If we're charging we will wake
+ * up on the FG IRQ
+ */
+ if (!di->flags.charging) {
+ ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_WAKEUP);
+ queue_work(di->fg_wq, &di->fg_work);
+ }
+
+ return 0;
+}
+
+static int ab8500_fg_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct ab8500_fg *di = platform_get_drvdata(pdev);
+
+ flush_delayed_work(&di->fg_periodic_work);
+
+ /*
+ * If the FG is enabled we will disable it before going to suspend
+ * only if we're not charging
+ */
+ if (di->flags.fg_enabled && !di->flags.charging)
+ ab8500_fg_coulomb_counter(di, false);
+
+ return 0;
+}
+#else
+#define ab8500_fg_suspend NULL
+#define ab8500_fg_resume NULL
+#endif
+
+static int __devexit ab8500_fg_remove(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct ab8500_fg *di = platform_get_drvdata(pdev);
+
+ list_del(&di->node);
+
+ /* Disable coulomb counter */
+ ret = ab8500_fg_coulomb_counter(di, false);
+ if (ret)
+ dev_err(di->dev, "failed to disable coulomb counter\n");
+
+ destroy_workqueue(di->fg_wq);
+ ab8500_fg_sysfs_exit(di);
+
+ flush_scheduled_work();
+ power_supply_unregister(&di->fg_psy);
+ platform_set_drvdata(pdev, NULL);
+ kfree(di);
+ return ret;
+}
+
+/* ab8500 fg driver interrupts and their respective isr */
+static struct ab8500_fg_interrupts ab8500_fg_irq[] = {
+ {"NCONV_ACCU", ab8500_fg_cc_convend_handler},
+ {"BATT_OVV", ab8500_fg_batt_ovv_handler},
+ {"LOW_BAT_F", ab8500_fg_lowbatf_handler},
+ {"CC_INT_CALIB", ab8500_fg_cc_int_calib_handler},
+ {"CCEOC", ab8500_fg_cc_data_end_handler},
+};
+
+static int __devinit ab8500_fg_probe(struct platform_device *pdev)
+{
+ int i, irq;
+ int ret = 0;
+ struct abx500_bm_plat_data *plat_data;
+
+ struct ab8500_fg *di =
+ kzalloc(sizeof(struct ab8500_fg), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ mutex_init(&di->cc_lock);
+
+ /* get parent data */
+ di->dev = &pdev->dev;
+ di->parent = dev_get_drvdata(pdev->dev.parent);
+ di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+
+ /* get fg specific platform data */
+ plat_data = pdev->dev.platform_data;
+ di->pdata = plat_data->fg;
+ if (!di->pdata) {
+ dev_err(di->dev, "no fg platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ /* get battery specific platform data */
+ di->bat = plat_data->battery;
+ if (!di->bat) {
+ dev_err(di->dev, "no battery platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ di->fg_psy.name = "ab8500_fg";
+ di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->fg_psy.properties = ab8500_fg_props;
+ di->fg_psy.num_properties = ARRAY_SIZE(ab8500_fg_props);
+ di->fg_psy.get_property = ab8500_fg_get_property;
+ di->fg_psy.supplied_to = di->pdata->supplied_to;
+ di->fg_psy.num_supplicants = di->pdata->num_supplicants;
+ di->fg_psy.external_power_changed = ab8500_fg_external_power_changed;
+
+ di->bat_cap.max_mah_design = MILLI_TO_MICRO *
+ di->bat->bat_type[di->bat->batt_id].charge_full_design;
+
+ di->bat_cap.max_mah = di->bat_cap.max_mah_design;
+
+ di->vbat_nom = di->bat->bat_type[di->bat->batt_id].nominal_voltage;
+
+ di->init_capacity = true;
+
+ ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+ ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
+
+ /* Create a work queue for running the FG algorithm */
+ di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq");
+ if (di->fg_wq == NULL) {
+ dev_err(di->dev, "failed to create work queue\n");
+ goto free_device_info;
+ }
+
+ /* Init work for running the fg algorithm instantly */
+ INIT_WORK(&di->fg_work, ab8500_fg_instant_work);
+
+ /* Init work for getting the battery accumulated current */
+ INIT_WORK(&di->fg_acc_cur_work, ab8500_fg_acc_cur_work);
+
+ /* Init work for reinitialising the fg algorithm */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->fg_reinit_work,
+ ab8500_fg_reinit_work);
+
+ /* Work delayed Queue to run the state machine */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->fg_periodic_work,
+ ab8500_fg_periodic_work);
+
+ /* Work to check low battery condition */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->fg_low_bat_work,
+ ab8500_fg_low_bat_work);
+
+ /* Init work for HW failure check */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->fg_check_hw_failure_work,
+ ab8500_fg_check_hw_failure_work);
+
+ /* Initialize OVV, and other registers */
+ ret = ab8500_fg_init_hw_registers(di);
+ if (ret) {
+ dev_err(di->dev, "failed to initialize registers\n");
+ goto free_inst_curr_wq;
+ }
+
+ /* Consider battery unknown until we're informed otherwise */
+ di->flags.batt_unknown = true;
+ di->flags.batt_id_received = false;
+
+ /* Register FG power supply class */
+ ret = power_supply_register(di->dev, &di->fg_psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register FG psy\n");
+ goto free_inst_curr_wq;
+ }
+
+ di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer);
+ ab8500_fg_coulomb_counter(di, true);
+
+ /* Initialize completion used to notify completion of inst current */
+ init_completion(&di->ab8500_fg_complete);
+
+ /* Register interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name);
+ ret = request_threaded_irq(irq, NULL, ab8500_fg_irq[i].isr,
+ IRQF_SHARED | IRQF_NO_SUSPEND,
+ ab8500_fg_irq[i].name, di);
+
+ if (ret != 0) {
+ dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+ , ab8500_fg_irq[i].name, irq, ret);
+ goto free_irq;
+ }
+ dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+ ab8500_fg_irq[i].name, irq, ret);
+ }
+ di->irq = platform_get_irq_byname(pdev, "CCEOC");
+ disable_irq(di->irq);
+
+ platform_set_drvdata(pdev, di);
+
+ ret = ab8500_fg_sysfs_init(di);
+ if (ret) {
+ dev_err(di->dev, "failed to create sysfs entry\n");
+ goto free_irq;
+ }
+
+ /* Calibrate the fg first time */
+ di->flags.calibrate = true;
+ di->calib_state = AB8500_FG_CALIB_INIT;
+
+ /* Use room temp as default value until we get an update from driver. */
+ di->bat_temp = 210;
+
+ /* Run the FG algorithm */
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+
+ list_add_tail(&di->node, &ab8500_fg_list);
+
+ return ret;
+
+free_irq:
+ power_supply_unregister(&di->fg_psy);
+
+ /* We also have to free all successfully registered irqs */
+ for (i = i - 1; i >= 0; i--) {
+ irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name);
+ free_irq(irq, di);
+ }
+free_inst_curr_wq:
+ destroy_workqueue(di->fg_wq);
+free_device_info:
+ kfree(di);
+
+ return ret;
+}
+
+static struct platform_driver ab8500_fg_driver = {
+ .probe = ab8500_fg_probe,
+ .remove = __devexit_p(ab8500_fg_remove),
+ .suspend = ab8500_fg_suspend,
+ .resume = ab8500_fg_resume,
+ .driver = {
+ .name = "ab8500-fg",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab8500_fg_init(void)
+{
+ return platform_driver_register(&ab8500_fg_driver);
+}
+
+static void __exit ab8500_fg_exit(void)
+{
+ platform_driver_unregister(&ab8500_fg_driver);
+}
+
+subsys_initcall_sync(ab8500_fg_init);
+module_exit(ab8500_fg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
+MODULE_ALIAS("platform:ab8500-fg");
+MODULE_DESCRIPTION("AB8500 Fuel Gauge driver");
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
new file mode 100644
index 0000000..804b88c
--- /dev/null
+++ b/drivers/power/abx500_chargalg.c
@@ -0,0 +1,1921 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Charging algorithm driver for abx500 variants
+ *
+ * License Terms: GNU General Public License v2
+ * Authors:
+ * Johan Palsson <johan.palsson@stericsson.com>
+ * Karl Komierowski <karl.komierowski@stericsson.com>
+ * Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ux500_chargalg.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+
+/* Watchdog kick interval */
+#define CHG_WD_INTERVAL (6 * HZ)
+
+/* End-of-charge criteria counter */
+#define EOC_COND_CNT 10
+
+/* Recharge criteria counter */
+#define RCH_COND_CNT 3
+
+#define to_abx500_chargalg_device_info(x) container_of((x), \
+ struct abx500_chargalg, chargalg_psy);
+
+enum abx500_chargers {
+ NO_CHG,
+ AC_CHG,
+ USB_CHG,
+};
+
+struct abx500_chargalg_charger_info {
+ enum abx500_chargers conn_chg;
+ enum abx500_chargers prev_conn_chg;
+ enum abx500_chargers online_chg;
+ enum abx500_chargers prev_online_chg;
+ enum abx500_chargers charger_type;
+ bool usb_chg_ok;
+ bool ac_chg_ok;
+ int usb_volt;
+ int usb_curr;
+ int ac_volt;
+ int ac_curr;
+ int usb_vset;
+ int usb_iset;
+ int ac_vset;
+ int ac_iset;
+};
+
+struct abx500_chargalg_suspension_status {
+ bool suspended_change;
+ bool ac_suspended;
+ bool usb_suspended;
+};
+
+struct abx500_chargalg_battery_data {
+ int temp;
+ int volt;
+ int avg_curr;
+ int inst_curr;
+ int percent;
+};
+
+enum abx500_chargalg_states {
+ STATE_HANDHELD_INIT,
+ STATE_HANDHELD,
+ STATE_CHG_NOT_OK_INIT,
+ STATE_CHG_NOT_OK,
+ STATE_HW_TEMP_PROTECT_INIT,
+ STATE_HW_TEMP_PROTECT,
+ STATE_NORMAL_INIT,
+ STATE_NORMAL,
+ STATE_WAIT_FOR_RECHARGE_INIT,
+ STATE_WAIT_FOR_RECHARGE,
+ STATE_MAINTENANCE_A_INIT,
+ STATE_MAINTENANCE_A,
+ STATE_MAINTENANCE_B_INIT,
+ STATE_MAINTENANCE_B,
+ STATE_TEMP_UNDEROVER_INIT,
+ STATE_TEMP_UNDEROVER,
+ STATE_TEMP_LOWHIGH_INIT,
+ STATE_TEMP_LOWHIGH,
+ STATE_SUSPENDED_INIT,
+ STATE_SUSPENDED,
+ STATE_OVV_PROTECT_INIT,
+ STATE_OVV_PROTECT,
+ STATE_SAFETY_TIMER_EXPIRED_INIT,
+ STATE_SAFETY_TIMER_EXPIRED,
+ STATE_BATT_REMOVED_INIT,
+ STATE_BATT_REMOVED,
+ STATE_WD_EXPIRED_INIT,
+ STATE_WD_EXPIRED,
+};
+
+static const char *states[] = {
+ "HANDHELD_INIT",
+ "HANDHELD",
+ "CHG_NOT_OK_INIT",
+ "CHG_NOT_OK",
+ "HW_TEMP_PROTECT_INIT",
+ "HW_TEMP_PROTECT",
+ "NORMAL_INIT",
+ "NORMAL",
+ "WAIT_FOR_RECHARGE_INIT",
+ "WAIT_FOR_RECHARGE",
+ "MAINTENANCE_A_INIT",
+ "MAINTENANCE_A",
+ "MAINTENANCE_B_INIT",
+ "MAINTENANCE_B",
+ "TEMP_UNDEROVER_INIT",
+ "TEMP_UNDEROVER",
+ "TEMP_LOWHIGH_INIT",
+ "TEMP_LOWHIGH",
+ "SUSPENDED_INIT",
+ "SUSPENDED",
+ "OVV_PROTECT_INIT",
+ "OVV_PROTECT",
+ "SAFETY_TIMER_EXPIRED_INIT",
+ "SAFETY_TIMER_EXPIRED",
+ "BATT_REMOVED_INIT",
+ "BATT_REMOVED",
+ "WD_EXPIRED_INIT",
+ "WD_EXPIRED",
+};
+
+struct abx500_chargalg_events {
+ bool batt_unknown;
+ bool mainextchnotok;
+ bool batt_ovv;
+ bool batt_rem;
+ bool btemp_underover;
+ bool btemp_lowhigh;
+ bool main_thermal_prot;
+ bool usb_thermal_prot;
+ bool main_ovv;
+ bool vbus_ovv;
+ bool usbchargernotok;
+ bool safety_timer_expired;
+ bool maintenance_timer_expired;
+ bool ac_wd_expired;
+ bool usb_wd_expired;
+ bool ac_cv_active;
+ bool usb_cv_active;
+ bool vbus_collapsed;
+};
+
+/**
+ * struct abx500_charge_curr_maximization - Charger maximization parameters
+ * @original_iset: the non optimized/maximised charger current
+ * @current_iset: the charging current used at this moment
+ * @test_delta_i: the delta between the current we want to charge and the
+ current that is really going into the battery
+ * @condition_cnt: number of iterations needed before a new charger current
+ is set
+ * @max_current: maximum charger current
+ * @wait_cnt: to avoid too fast current step down in case of charger
+ * voltage collapse, we insert this delay between step
+ * down
+ * @level: tells in how many steps the charging current has been
+ increased
+ */
+struct abx500_charge_curr_maximization {
+ int original_iset;
+ int current_iset;
+ int test_delta_i;
+ int condition_cnt;
+ int max_current;
+ int wait_cnt;
+ u8 level;
+};
+
+enum maxim_ret {
+ MAXIM_RET_NOACTION,
+ MAXIM_RET_CHANGE,
+ MAXIM_RET_IBAT_TOO_HIGH,
+};
+
+/**
+ * struct abx500_chargalg - abx500 Charging algorithm device information
+ * @dev: pointer to the structure device
+ * @charge_status: battery operating status
+ * @eoc_cnt: counter used to determine end-of_charge
+ * @rch_cnt: counter used to determine start of recharge
+ * @maintenance_chg: indicate if maintenance charge is active
+ * @t_hyst_norm temperature hysteresis when the temperature has been
+ * over or under normal limits
+ * @t_hyst_lowhigh temperature hysteresis when the temperature has been
+ * over or under the high or low limits
+ * @charge_state: current state of the charging algorithm
+ * @ccm charging current maximization parameters
+ * @chg_info: information about connected charger types
+ * @batt_data: data of the battery
+ * @susp_status: current charger suspension status
+ * @pdata: pointer to the abx500_chargalg platform data
+ * @bat: pointer to the abx500_bm platform data
+ * @chargalg_psy: structure that holds the battery properties exposed by
+ * the charging algorithm
+ * @events: structure for information about events triggered
+ * @chargalg_wq: work queue for running the charging algorithm
+ * @chargalg_periodic_work: work to run the charging algorithm periodically
+ * @chargalg_wd_work: work to kick the charger watchdog periodically
+ * @chargalg_work: work to run the charging algorithm instantly
+ * @safety_timer: charging safety timer
+ * @maintenance_timer: maintenance charging timer
+ * @chargalg_kobject: structure of type kobject
+ */
+struct abx500_chargalg {
+ struct device *dev;
+ int charge_status;
+ int eoc_cnt;
+ int rch_cnt;
+ bool maintenance_chg;
+ int t_hyst_norm;
+ int t_hyst_lowhigh;
+ enum abx500_chargalg_states charge_state;
+ struct abx500_charge_curr_maximization ccm;
+ struct abx500_chargalg_charger_info chg_info;
+ struct abx500_chargalg_battery_data batt_data;
+ struct abx500_chargalg_suspension_status susp_status;
+ struct abx500_chargalg_platform_data *pdata;
+ struct abx500_bm_data *bat;
+ struct power_supply chargalg_psy;
+ struct ux500_charger *ac_chg;
+ struct ux500_charger *usb_chg;
+ struct abx500_chargalg_events events;
+ struct workqueue_struct *chargalg_wq;
+ struct delayed_work chargalg_periodic_work;
+ struct delayed_work chargalg_wd_work;
+ struct work_struct chargalg_work;
+ struct timer_list safety_timer;
+ struct timer_list maintenance_timer;
+ struct kobject chargalg_kobject;
+};
+
+/* Main battery properties */
+static enum power_supply_property abx500_chargalg_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+};
+
+/**
+ * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
+ * @data: pointer to the abx500_chargalg structure
+ *
+ * This function gets called when the safety timer for the charger
+ * expires
+ */
+static void abx500_chargalg_safety_timer_expired(unsigned long data)
+{
+ struct abx500_chargalg *di = (struct abx500_chargalg *) data;
+ dev_err(di->dev, "Safety timer expired\n");
+ di->events.safety_timer_expired = true;
+
+ /* Trigger execution of the algorithm instantly */
+ queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * abx500_chargalg_maintenance_timer_expired() - Expiration of
+ * the maintenance timer
+ * @i: pointer to the abx500_chargalg structure
+ *
+ * This function gets called when the maintenence timer
+ * expires
+ */
+static void abx500_chargalg_maintenance_timer_expired(unsigned long data)
+{
+
+ struct abx500_chargalg *di = (struct abx500_chargalg *) data;
+ dev_dbg(di->dev, "Maintenance timer expired\n");
+ di->events.maintenance_timer_expired = true;
+
+ /* Trigger execution of the algorithm instantly */
+ queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * abx500_chargalg_state_to() - Change charge state
+ * @di: pointer to the abx500_chargalg structure
+ *
+ * This function gets called when a charge state change should occur
+ */
+static void abx500_chargalg_state_to(struct abx500_chargalg *di,
+ enum abx500_chargalg_states state)
+{
+ dev_dbg(di->dev,
+ "State changed: %s (From state: [%d] %s =to=> [%d] %s )\n",
+ di->charge_state == state ? "NO" : "YES",
+ di->charge_state,
+ states[di->charge_state],
+ state,
+ states[state]);
+
+ di->charge_state = state;
+}
+
+/**
+ * abx500_chargalg_check_charger_connection() - Check charger connection change
+ * @di: pointer to the abx500_chargalg structure
+ *
+ * This function will check if there is a change in the charger connection
+ * and change charge state accordingly. AC has precedence over USB.
+ */
+static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
+{
+ if (di->chg_info.conn_chg != di->chg_info.prev_conn_chg ||
+ di->susp_status.suspended_change) {
+ /*
+ * Charger state changed or suspension
+ * has changed since last update
+ */
+ if ((di->chg_info.conn_chg & AC_CHG) &&
+ !di->susp_status.ac_suspended) {
+ dev_dbg(di->dev, "Charging source is AC\n");
+ if (di->chg_info.charger_type != AC_CHG) {
+ di->chg_info.charger_type = AC_CHG;
+ abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ }
+ } else if ((di->chg_info.conn_chg & USB_CHG) &&
+ !di->susp_status.usb_suspended) {
+ dev_dbg(di->dev, "Charging source is USB\n");
+ di->chg_info.charger_type = USB_CHG;
+ abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ } else if (di->chg_info.conn_chg &&
+ (di->susp_status.ac_suspended ||
+ di->susp_status.usb_suspended)) {
+ dev_dbg(di->dev, "Charging is suspended\n");
+ di->chg_info.charger_type = NO_CHG;
+ abx500_chargalg_state_to(di, STATE_SUSPENDED_INIT);
+ } else {
+ dev_dbg(di->dev, "Charging source is OFF\n");
+ di->chg_info.charger_type = NO_CHG;
+ abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
+ }
+ di->chg_info.prev_conn_chg = di->chg_info.conn_chg;
+ di->susp_status.suspended_change = false;
+ }
+ return di->chg_info.conn_chg;
+}
+
+/**
+ * abx500_chargalg_start_safety_timer() - Start charging safety timer
+ * @di: pointer to the abx500_chargalg structure
+ *
+ * The safety timer is used to avoid overcharging of old or bad batteries.
+ * There are different timers for AC and USB
+ */
+static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
+{
+ unsigned long timer_expiration = 0;
+
+ switch (di->chg_info.charger_type) {
+ case AC_CHG:
+ timer_expiration =
+ round_jiffies(jiffies +
+ (di->bat->main_safety_tmr_h * 3600 * HZ));
+ break;
+
+ case USB_CHG:
+ timer_expiration =
+ round_jiffies(jiffies +
+ (di->bat->usb_safety_tmr_h * 3600 * HZ));
+ break;
+
+ default:
+ dev_err(di->dev, "Unknown charger to charge from\n");
+ break;
+ }
+
+ di->events.safety_timer_expired = false;
+ di->safety_timer.expires = timer_expiration;
+ if (!timer_pending(&di->safety_timer))
+ add_timer(&di->safety_timer);
+ else
+ mod_timer(&di->safety_timer, timer_expiration);
+}
+
+/**
+ * abx500_chargalg_stop_safety_timer() - Stop charging safety timer
+ * @di: pointer to the abx500_chargalg structure
+ *
+ * The safety timer is stopped whenever the NORMAL state is exited
+ */
+static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
+{
+ di->events.safety_timer_expired = false;
+ del_timer(&di->safety_timer);
+}
+
+/**
+ * abx500_chargalg_start_maintenance_timer() - Start charging maintenance timer
+ * @di: pointer to the abx500_chargalg structure
+ * @duration: duration of ther maintenance timer in hours
+ *
+ * The maintenance timer is used to maintain the charge in the battery once
+ * the battery is considered full. These timers are chosen to match the
+ * discharge curve of the battery
+ */
+static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
+ int duration)
+{
+ unsigned long timer_expiration;
+
+ /* Convert from hours to jiffies */
+ timer_expiration = round_jiffies(jiffies + (duration * 3600 * HZ));
+
+ di->events.maintenance_timer_expired = false;
+ di->maintenance_timer.expires = timer_expiration;
+ if (!timer_pending(&di->maintenance_timer))
+ add_timer(&di->maintenance_timer);
+ else
+ mod_timer(&di->maintenance_timer, timer_expiration);
+}
+
+/**
+ * abx500_chargalg_stop_maintenance_timer() - Stop maintenance timer
+ * @di: pointer to the abx500_chargalg structure
+ *
+ * The maintenance timer is stopped whenever maintenance ends or when another
+ * state is entered
+ */
+static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di)
+{
+ di->events.maintenance_timer_expired = false;
+ del_timer(&di->maintenance_timer);
+}
+
+/**
+ * abx500_chargalg_kick_watchdog() - Kick charger watchdog
+ * @di: pointer to the abx500_chargalg structure
+ *
+ * The charger watchdog have to be kicked periodically whenever the charger is
+ * on, else the ABB will reset the system
+ */
+static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
+{
+ /* Check if charger exists and kick watchdog if charging */
+ if (di->ac_chg && di->ac_chg->ops.kick_wd &&
+ di->chg_info.online_chg & AC_CHG)
+ return di->ac_chg->ops.kick_wd(di->ac_chg);
+ else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
+ di->chg_info.online_chg & USB_CHG)
+ return di->usb_chg->ops.kick_wd(di->usb_chg);
+
+ return -ENXIO;
+}
+
+/**
+ * abx500_chargalg_ac_en() - Turn on/off the AC charger
+ * @di: pointer to the abx500_chargalg structure
+ * @enable: charger on/off
+ * @vset: requested charger output voltage
+ * @iset: requested charger output current
+ *
+ * The AC charger will be turned on/off with the requested charge voltage and
+ * current
+ */
+static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
+ int vset, int iset)
+{
+ if (!di->ac_chg || !di->ac_chg->ops.enable)
+ return -ENXIO;
+
+ /* Select maximum of what both the charger and the battery supports */
+ if (di->ac_chg->max_out_volt)
+ vset = min(vset, di->ac_chg->max_out_volt);
+ if (di->ac_chg->max_out_curr)
+ iset = min(iset, di->ac_chg->max_out_curr);
+
+ di->chg_info.ac_iset = iset;
+ di->chg_info.ac_vset = vset;
+
+ return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
+}
+
+/**
+ * abx500_chargalg_usb_en() - Turn on/off the USB charger
+ * @di: pointer to the abx500_chargalg structure
+ * @enable: charger on/off
+ * @vset: requested charger output voltage
+ * @iset: requested charger output current
+ *
+ * The USB charger will be turned on/off with the requested charge voltage and
+ * current
+ */
+static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable,
+ int vset, int iset)
+{
+ if (!di->usb_chg || !di->usb_chg->ops.enable)
+ return -ENXIO;
+
+ /* Select maximum of what both the charger and the battery supports */
+ if (di->usb_chg->max_out_volt)
+ vset = min(vset, di->usb_chg->max_out_volt);
+ if (di->usb_chg->max_out_curr)
+ iset = min(iset, di->usb_chg->max_out_curr);
+
+ di->chg_info.usb_iset = iset;
+ di->chg_info.usb_vset = vset;
+
+ return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset);
+}
+
+/**
+ * abx500_chargalg_update_chg_curr() - Update charger current
+ * @di: pointer to the abx500_chargalg structure
+ * @iset: requested charger output current
+ *
+ * The charger output current will be updated for the charger
+ * that is currently in use
+ */
+static int abx500_chargalg_update_chg_curr(struct abx500_chargalg *di,
+ int iset)
+{
+ /* Check if charger exists and update current if charging */
+ if (di->ac_chg && di->ac_chg->ops.update_curr &&
+ di->chg_info.charger_type & AC_CHG) {
+ /*
+ * Select maximum of what both the charger
+ * and the battery supports
+ */
+ if (di->ac_chg->max_out_curr)
+ iset = min(iset, di->ac_chg->max_out_curr);
+
+ di->chg_info.ac_iset = iset;
+
+ return di->ac_chg->ops.update_curr(di->ac_chg, iset);
+ } else if (di->usb_chg && di->usb_chg->ops.update_curr &&
+ di->chg_info.charger_type & USB_CHG) {
+ /*
+ * Select maximum of what both the charger
+ * and the battery supports
+ */
+ if (di->usb_chg->max_out_curr)
+ iset = min(iset, di->usb_chg->max_out_curr);
+
+ di->chg_info.usb_iset = iset;
+
+ return di->usb_chg->ops.update_curr(di->usb_chg, iset);
+ }
+
+ return -ENXIO;
+}
+
+/**
+ * abx500_chargalg_stop_charging() - Stop charging
+ * @di: pointer to the abx500_chargalg structure
+ *
+ * This function is called from any state where charging should be stopped.
+ * All charging is disabled and all status parameters and timers are changed
+ * accordingly
+ */
+static void abx500_chargalg_stop_charging(struct abx500_chargalg *di)
+{
+ abx500_chargalg_ac_en(di, false, 0, 0);
+ abx500_chargalg_usb_en(di, false, 0, 0);
+ abx500_chargalg_stop_safety_timer(di);
+ abx500_chargalg_stop_maintenance_timer(di);
+ di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ di->maintenance_chg = false;
+ cancel_delayed_work(&di->chargalg_wd_work);
+ power_supply_changed(&di->chargalg_psy);
+}
+
+/**
+ * abx500_chargalg_hold_charging() - Pauses charging
+ * @di: pointer to the abx500_chargalg structure
+ *
+ * This function is called in the case where maintenance charging has been
+ * disabled and instead a battery voltage mode is entered to check when the
+ * battery voltage has reached a certain recharge voltage
+ */
+static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
+{
+ abx500_chargalg_ac_en(di, false, 0, 0);
+ abx500_chargalg_usb_en(di, false, 0, 0);
+ abx500_chargalg_stop_safety_timer(di);
+ abx500_chargalg_stop_maintenance_timer(di);
+ di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+ di->maintenance_chg = false;
+ cancel_delayed_work(&di->chargalg_wd_work);
+ power_supply_changed(&di->chargalg_psy);
+}
+
+/**
+ * abx500_chargalg_start_charging() - Start the charger
+ * @di: pointer to the abx500_chargalg structure
+ * @vset: requested charger output voltage
+ * @iset: requested charger output current
+ *
+ * A charger will be enabled depending on the requested charger type that was
+ * detected previously.
+ */
+static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
+ int vset, int iset)
+{
+ switch (di->chg_info.charger_type) {
+ case AC_CHG:
+ dev_dbg(di->dev,
+ "AC parameters: Vset %d, Ich %d\n", vset, iset);
+ abx500_chargalg_usb_en(di, false, 0, 0);
+ abx500_chargalg_ac_en(di, true, vset, iset);
+ break;
+
+ case USB_CHG:
+ dev_dbg(di->dev,
+ "USB parameters: Vset %d, Ich %d\n", vset, iset);
+ abx500_chargalg_ac_en(di, false, 0, 0);
+ abx500_chargalg_usb_en(di, true, vset, iset);
+ break;
+
+ default:
+ dev_err(di->dev, "Unknown charger to charge from\n");
+ break;
+ }
+}
+
+/**
+ * abx500_chargalg_check_temp() - Check battery temperature ranges
+ * @di: pointer to the abx500_chargalg structure
+ *
+ * The battery temperature is checked against the predefined limits and the
+ * charge state is changed accordingly
+ */
+static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
+{
+ if (di->batt_data.temp > (di->bat->temp_low + di->t_hyst_norm) &&
+ di->batt_data.temp < (di->bat->temp_high - di->t_hyst_norm)) {
+ /* Temp OK! */
+ di->events.btemp_underover = false;
+ di->events.btemp_lowhigh = false;
+ di->t_hyst_norm = 0;
+ di->t_hyst_lowhigh = 0;
+ } else {
+ if (((di->batt_data.temp >= di->bat->temp_high) &&
+ (di->batt_data.temp <
+ (di->bat->temp_over - di->t_hyst_lowhigh))) ||
+ ((di->batt_data.temp >
+ (di->bat->temp_under + di->t_hyst_lowhigh)) &&
+ (di->batt_data.temp <= di->bat->temp_low))) {
+ /* TEMP minor!!!!! */
+ di->events.btemp_underover = false;
+ di->events.btemp_lowhigh = true;
+ di->t_hyst_norm = di->bat->temp_hysteresis;
+ di->t_hyst_lowhigh = 0;
+ } else if (di->batt_data.temp <= di->bat->temp_under ||
+ di->batt_data.temp >= di->bat->temp_over) {
+ /* TEMP major!!!!! */
+ di->events.btemp_underover = true;
+ di->events.btemp_lowhigh = false;
+ di->t_hyst_norm = 0;
+ di->t_hyst_lowhigh = di->bat->temp_hysteresis;
+ } else {
+ /* Within hysteresis */
+ dev_dbg(di->dev, "Within hysteresis limit temp: %d "
+ "hyst_lowhigh %d, hyst normal %d\n",
+ di->batt_data.temp, di->t_hyst_lowhigh,
+ di->t_hyst_norm);
+ }
+ }
+}
+
+/**
+ * abx500_chargalg_check_charger_voltage() - Check charger voltage
+ * @di: pointer to the abx500_chargalg structure
+ *
+ * Charger voltage is checked against maximum limit
+ */
+static void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di)
+{
+ if (di->chg_info.usb_volt > di->bat->chg_params->usb_volt_max)
+ di->chg_info.usb_chg_ok = false;
+ else
+ di->chg_info.usb_chg_ok = true;
+
+ if (di->chg_info.ac_volt > di->bat->chg_params->ac_volt_max)
+ di->chg_info.ac_chg_ok = false;
+ else
+ di->chg_info.ac_chg_ok = true;
+
+}
+
+/**
+ * abx500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled
+ * @di: pointer to the abx500_chargalg structure
+ *
+ * End-of-charge criteria is fulfilled when the battery voltage is above a
+ * certain limit and the battery current is below a certain limit for a
+ * predefined number of consecutive seconds. If true, the battery is full
+ */
+static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
+{
+ if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
+ di->charge_state == STATE_NORMAL &&
+ !di->maintenance_chg && (di->batt_data.volt >=
+ di->bat->bat_type[di->bat->batt_id].termination_vol ||
+ di->events.usb_cv_active || di->events.ac_cv_active) &&
+ di->batt_data.avg_curr <
+ di->bat->bat_type[di->bat->batt_id].termination_curr &&
+ di->batt_data.avg_curr > 0) {
+ if (++di->eoc_cnt >= EOC_COND_CNT) {
+ di->eoc_cnt = 0;
+ di->charge_status = POWER_SUPPLY_STATUS_FULL;
+ di->maintenance_chg = true;
+ dev_dbg(di->dev, "EOC reached!\n");
+ power_supply_changed(&di->chargalg_psy);
+ } else {
+ dev_dbg(di->dev,
+ " EOC limit reached for the %d"
+ " time, out of %d before EOC\n",
+ di->eoc_cnt,
+ EOC_COND_CNT);
+ }
+ } else {
+ di->eoc_cnt = 0;
+ }
+}
+
+static void init_maxim_chg_curr(struct abx500_chargalg *di)
+{
+ di->ccm.original_iset =
+ di->bat->bat_type[di->bat->batt_id].normal_cur_lvl;
+ di->ccm.current_iset =
+ di->bat->bat_type[di->bat->batt_id].normal_cur_lvl;
+ di->ccm.test_delta_i = di->bat->maxi->charger_curr_step;
+ di->ccm.max_current = di->bat->maxi->chg_curr;
+ di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+ di->ccm.level = 0;
+}
+
+/**
+ * abx500_chargalg_chg_curr_maxim - increases the charger current to
+ * compensate for the system load
+ * @di pointer to the abx500_chargalg structure
+ *
+ * This maximization function is used to raise the charger current to get the
+ * battery current as close to the optimal value as possible. The battery
+ * current during charging is affected by the system load
+ */
+static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
+{
+ int delta_i;
+
+ if (!di->bat->maxi->ena_maxi)
+ return MAXIM_RET_NOACTION;
+
+ delta_i = di->ccm.original_iset - di->batt_data.inst_curr;
+
+ if (di->events.vbus_collapsed) {
+ dev_dbg(di->dev, "Charger voltage has collapsed %d\n",
+ di->ccm.wait_cnt);
+ if (di->ccm.wait_cnt == 0) {
+ dev_dbg(di->dev, "lowering current\n");
+ di->ccm.wait_cnt++;
+ di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+ di->ccm.max_current =
+ di->ccm.current_iset - di->ccm.test_delta_i;
+ di->ccm.current_iset = di->ccm.max_current;
+ di->ccm.level--;
+ return MAXIM_RET_CHANGE;
+ } else {
+ dev_dbg(di->dev, "waiting\n");
+ /* Let's go in here twice before lowering curr again */
+ di->ccm.wait_cnt = (di->ccm.wait_cnt + 1) % 3;
+ return MAXIM_RET_NOACTION;
+ }
+ }
+
+ di->ccm.wait_cnt = 0;
+
+ if ((di->batt_data.inst_curr > di->ccm.original_iset)) {
+ dev_dbg(di->dev, " Maximization Ibat (%dmA) too high"
+ " (limit %dmA) (current iset: %dmA)!\n",
+ di->batt_data.inst_curr, di->ccm.original_iset,
+ di->ccm.current_iset);
+
+ if (di->ccm.current_iset == di->ccm.original_iset)
+ return MAXIM_RET_NOACTION;
+
+ di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+ di->ccm.current_iset = di->ccm.original_iset;
+ di->ccm.level = 0;
+
+ return MAXIM_RET_IBAT_TOO_HIGH;
+ }
+
+ if (delta_i > di->ccm.test_delta_i &&
+ (di->ccm.current_iset + di->ccm.test_delta_i) <
+ di->ccm.max_current) {
+ if (di->ccm.condition_cnt-- == 0) {
+ /* Increse the iset with cco.test_delta_i */
+ di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+ di->ccm.current_iset += di->ccm.test_delta_i;
+ di->ccm.level++;
+ dev_dbg(di->dev, " Maximization needed, increase"
+ " with %d mA to %dmA (Optimal ibat: %d)"
+ " Level %d\n",
+ di->ccm.test_delta_i,
+ di->ccm.current_iset,
+ di->ccm.original_iset,
+ di->ccm.level);
+ return MAXIM_RET_CHANGE;
+ } else {
+ return MAXIM_RET_NOACTION;
+ }
+ } else {
+ di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+ return MAXIM_RET_NOACTION;
+ }
+}
+
+static void handle_maxim_chg_curr(struct abx500_chargalg *di)
+{
+ enum maxim_ret ret;
+ int result;
+
+ ret = abx500_chargalg_chg_curr_maxim(di);
+ switch (ret) {
+ case MAXIM_RET_CHANGE:
+ result = abx500_chargalg_update_chg_curr(di,
+ di->ccm.current_iset);
+ if (result)
+ dev_err(di->dev, "failed to set chg curr\n");
+ break;
+ case MAXIM_RET_IBAT_TOO_HIGH:
+ result = abx500_chargalg_update_chg_curr(di,
+ di->bat->bat_type[di->bat->batt_id].normal_cur_lvl);
+ if (result)
+ dev_err(di->dev, "failed to set chg curr\n");
+ break;
+
+ case MAXIM_RET_NOACTION:
+ default:
+ /* Do nothing..*/
+ break;
+ }
+}
+
+static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
+{
+ struct power_supply *psy;
+ struct power_supply *ext;
+ struct abx500_chargalg *di;
+ union power_supply_propval ret;
+ int i, j;
+ bool psy_found = false;
+
+ psy = (struct power_supply *)data;
+ ext = dev_get_drvdata(dev);
+ di = to_abx500_chargalg_device_info(psy);
+ /* For all psy where the driver name appears in any supplied_to */
+ for (i = 0; i < ext->num_supplicants; i++) {
+ if (!strcmp(ext->supplied_to[i], psy->name))
+ psy_found = true;
+ }
+ if (!psy_found)
+ return 0;
+
+ /* Go through all properties for the psy */
+ for (j = 0; j < ext->num_properties; j++) {
+ enum power_supply_property prop;
+ prop = ext->properties[j];
+
+ /* Initialize chargers if not already done */
+ if (!di->ac_chg &&
+ ext->type == POWER_SUPPLY_TYPE_MAINS)
+ di->ac_chg = psy_to_ux500_charger(ext);
+ else if (!di->usb_chg &&
+ ext->type == POWER_SUPPLY_TYPE_USB)
+ di->usb_chg = psy_to_ux500_charger(ext);
+
+ if (ext->get_property(ext, prop, &ret))
+ continue;
+ switch (prop) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ /* Battery present */
+ if (ret.intval)
+ di->events.batt_rem = false;
+ /* Battery removed */
+ else
+ di->events.batt_rem = true;
+ break;
+ case POWER_SUPPLY_TYPE_MAINS:
+ /* AC disconnected */
+ if (!ret.intval &&
+ (di->chg_info.conn_chg & AC_CHG)) {
+ di->chg_info.prev_conn_chg =
+ di->chg_info.conn_chg;
+ di->chg_info.conn_chg &= ~AC_CHG;
+ }
+ /* AC connected */
+ else if (ret.intval &&
+ !(di->chg_info.conn_chg & AC_CHG)) {
+ di->chg_info.prev_conn_chg =
+ di->chg_info.conn_chg;
+ di->chg_info.conn_chg |= AC_CHG;
+ }
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ /* USB disconnected */
+ if (!ret.intval &&
+ (di->chg_info.conn_chg & USB_CHG)) {
+ di->chg_info.prev_conn_chg =
+ di->chg_info.conn_chg;
+ di->chg_info.conn_chg &= ~USB_CHG;
+ }
+ /* USB connected */
+ else if (ret.intval &&
+ !(di->chg_info.conn_chg & USB_CHG)) {
+ di->chg_info.prev_conn_chg =
+ di->chg_info.conn_chg;
+ di->chg_info.conn_chg |= USB_CHG;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_ONLINE:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ break;
+ case POWER_SUPPLY_TYPE_MAINS:
+ /* AC offline */
+ if (!ret.intval &&
+ (di->chg_info.online_chg & AC_CHG)) {
+ di->chg_info.prev_online_chg =
+ di->chg_info.online_chg;
+ di->chg_info.online_chg &= ~AC_CHG;
+ }
+ /* AC online */
+ else if (ret.intval &&
+ !(di->chg_info.online_chg & AC_CHG)) {
+ di->chg_info.prev_online_chg =
+ di->chg_info.online_chg;
+ di->chg_info.online_chg |= AC_CHG;
+ queue_delayed_work(di->chargalg_wq,
+ &di->chargalg_wd_work, 0);
+ }
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ /* USB offline */
+ if (!ret.intval &&
+ (di->chg_info.online_chg & USB_CHG)) {
+ di->chg_info.prev_online_chg =
+ di->chg_info.online_chg;
+ di->chg_info.online_chg &= ~USB_CHG;
+ }
+ /* USB online */
+ else if (ret.intval &&
+ !(di->chg_info.online_chg & USB_CHG)) {
+ di->chg_info.prev_online_chg =
+ di->chg_info.online_chg;
+ di->chg_info.online_chg |= USB_CHG;
+ queue_delayed_work(di->chargalg_wq,
+ &di->chargalg_wd_work, 0);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_HEALTH:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ break;
+ case POWER_SUPPLY_TYPE_MAINS:
+ switch (ret.intval) {
+ case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
+ di->events.mainextchnotok = true;
+ di->events.main_thermal_prot = false;
+ di->events.main_ovv = false;
+ di->events.ac_wd_expired = false;
+ break;
+ case POWER_SUPPLY_HEALTH_DEAD:
+ di->events.ac_wd_expired = true;
+ di->events.mainextchnotok = false;
+ di->events.main_ovv = false;
+ di->events.main_thermal_prot = false;
+ break;
+ case POWER_SUPPLY_HEALTH_COLD:
+ case POWER_SUPPLY_HEALTH_OVERHEAT:
+ di->events.main_thermal_prot = true;
+ di->events.mainextchnotok = false;
+ di->events.main_ovv = false;
+ di->events.ac_wd_expired = false;
+ break;
+ case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+ di->events.main_ovv = true;
+ di->events.mainextchnotok = false;
+ di->events.main_thermal_prot = false;
+ di->events.ac_wd_expired = false;
+ break;
+ case POWER_SUPPLY_HEALTH_GOOD:
+ di->events.main_thermal_prot = false;
+ di->events.mainextchnotok = false;
+ di->events.main_ovv = false;
+ di->events.ac_wd_expired = false;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_TYPE_USB:
+ switch (ret.intval) {
+ case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
+ di->events.usbchargernotok = true;
+ di->events.usb_thermal_prot = false;
+ di->events.vbus_ovv = false;
+ di->events.usb_wd_expired = false;
+ break;
+ case POWER_SUPPLY_HEALTH_DEAD:
+ di->events.usb_wd_expired = true;
+ di->events.usbchargernotok = false;
+ di->events.usb_thermal_prot = false;
+ di->events.vbus_ovv = false;
+ break;
+ case POWER_SUPPLY_HEALTH_COLD:
+ case POWER_SUPPLY_HEALTH_OVERHEAT:
+ di->events.usb_thermal_prot = true;
+ di->events.usbchargernotok = false;
+ di->events.vbus_ovv = false;
+ di->events.usb_wd_expired = false;
+ break;
+ case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+ di->events.vbus_ovv = true;
+ di->events.usbchargernotok = false;
+ di->events.usb_thermal_prot = false;
+ di->events.usb_wd_expired = false;
+ break;
+ case POWER_SUPPLY_HEALTH_GOOD:
+ di->events.usbchargernotok = false;
+ di->events.usb_thermal_prot = false;
+ di->events.vbus_ovv = false;
+ di->events.usb_wd_expired = false;
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ di->batt_data.volt = ret.intval / 1000;
+ break;
+ case POWER_SUPPLY_TYPE_MAINS:
+ di->chg_info.ac_volt = ret.intval / 1000;
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ di->chg_info.usb_volt = ret.intval / 1000;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_MAINS:
+ /* AVG is used to indicate when we are
+ * in CV mode */
+ if (ret.intval)
+ di->events.ac_cv_active = true;
+ else
+ di->events.ac_cv_active = false;
+
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ /* AVG is used to indicate when we are
+ * in CV mode */
+ if (ret.intval)
+ di->events.usb_cv_active = true;
+ else
+ di->events.usb_cv_active = false;
+
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ if (ret.intval)
+ di->events.batt_unknown = false;
+ else
+ di->events.batt_unknown = true;
+
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_TEMP:
+ di->batt_data.temp = ret.intval / 10;
+ break;
+
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_MAINS:
+ di->chg_info.ac_curr =
+ ret.intval / 1000;
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ di->chg_info.usb_curr =
+ ret.intval / 1000;
+ break;
+ case POWER_SUPPLY_TYPE_BATTERY:
+ di->batt_data.inst_curr = ret.intval / 1000;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ di->batt_data.avg_curr = ret.intval / 1000;
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ if (ret.intval)
+ di->events.vbus_collapsed = true;
+ else
+ di->events.vbus_collapsed = false;
+ break;
+ default:
+ break;
+ }
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ di->batt_data.percent = ret.intval;
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * abx500_chargalg_external_power_changed() - callback for power supply changes
+ * @psy: pointer to the structure power_supply
+ *
+ * This function is the entry point of the pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in any external power
+ * supply that this driver needs to be notified of.
+ */
+static void abx500_chargalg_external_power_changed(struct power_supply *psy)
+{
+ struct abx500_chargalg *di = to_abx500_chargalg_device_info(psy);
+
+ /*
+ * Trigger execution of the algorithm instantly and read
+ * all power_supply properties there instead
+ */
+ queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * abx500_chargalg_algorithm() - Main function for the algorithm
+ * @di: pointer to the abx500_chargalg structure
+ *
+ * This is the main control function for the charging algorithm.
+ * It is called periodically or when something happens that will
+ * trigger a state change
+ */
+static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
+{
+ int charger_status;
+
+ /* Collect data from all power_supply class devices */
+ class_for_each_device(power_supply_class, NULL,
+ &di->chargalg_psy, abx500_chargalg_get_ext_psy_data);
+
+ abx500_chargalg_end_of_charge(di);
+ abx500_chargalg_check_temp(di);
+ abx500_chargalg_check_charger_voltage(di);
+
+ charger_status = abx500_chargalg_check_charger_connection(di);
+ /*
+ * First check if we have a charger connected.
+ * Also we don't allow charging of unknown batteries if configured
+ * this way
+ */
+ if (!charger_status ||
+ (di->events.batt_unknown && !di->bat->chg_unknown_bat)) {
+ if (di->charge_state != STATE_HANDHELD) {
+ di->events.safety_timer_expired = false;
+ abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
+ }
+ }
+
+ /* If suspended, we should not continue checking the flags */
+ else if (di->charge_state == STATE_SUSPENDED_INIT ||
+ di->charge_state == STATE_SUSPENDED) {
+ /* We don't do anything here, just don,t continue */
+ }
+
+ /* Safety timer expiration */
+ else if (di->events.safety_timer_expired) {
+ if (di->charge_state != STATE_SAFETY_TIMER_EXPIRED)
+ abx500_chargalg_state_to(di,
+ STATE_SAFETY_TIMER_EXPIRED_INIT);
+ }
+ /*
+ * Check if any interrupts has occured
+ * that will prevent us from charging
+ */
+
+ /* Battery removed */
+ else if (di->events.batt_rem) {
+ if (di->charge_state != STATE_BATT_REMOVED)
+ abx500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT);
+ }
+ /* Main or USB charger not ok. */
+ else if (di->events.mainextchnotok || di->events.usbchargernotok) {
+ /*
+ * If vbus_collapsed is set, we have to lower the charger
+ * current, which is done in the normal state below
+ */
+ if (di->charge_state != STATE_CHG_NOT_OK &&
+ !di->events.vbus_collapsed)
+ abx500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT);
+ }
+ /* VBUS, Main or VBAT OVV. */
+ else if (di->events.vbus_ovv ||
+ di->events.main_ovv ||
+ di->events.batt_ovv ||
+ !di->chg_info.usb_chg_ok ||
+ !di->chg_info.ac_chg_ok) {
+ if (di->charge_state != STATE_OVV_PROTECT)
+ abx500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT);
+ }
+ /* USB Thermal, stop charging */
+ else if (di->events.main_thermal_prot ||
+ di->events.usb_thermal_prot) {
+ if (di->charge_state != STATE_HW_TEMP_PROTECT)
+ abx500_chargalg_state_to(di,
+ STATE_HW_TEMP_PROTECT_INIT);
+ }
+ /* Battery temp over/under */
+ else if (di->events.btemp_underover) {
+ if (di->charge_state != STATE_TEMP_UNDEROVER)
+ abx500_chargalg_state_to(di,
+ STATE_TEMP_UNDEROVER_INIT);
+ }
+ /* Watchdog expired */
+ else if (di->events.ac_wd_expired ||
+ di->events.usb_wd_expired) {
+ if (di->charge_state != STATE_WD_EXPIRED)
+ abx500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT);
+ }
+ /* Battery temp high/low */
+ else if (di->events.btemp_lowhigh) {
+ if (di->charge_state != STATE_TEMP_LOWHIGH)
+ abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT);
+ }
+
+ dev_dbg(di->dev,
+ "[CHARGALG] Vb %d Ib_avg %d Ib_inst %d Tb %d Cap %d Maint %d "
+ "State %s Active_chg %d Chg_status %d AC %d USB %d "
+ "AC_online %d USB_online %d AC_CV %d USB_CV %d AC_I %d "
+ "USB_I %d AC_Vset %d AC_Iset %d USB_Vset %d USB_Iset %d\n",
+ di->batt_data.volt,
+ di->batt_data.avg_curr,
+ di->batt_data.inst_curr,
+ di->batt_data.temp,
+ di->batt_data.percent,
+ di->maintenance_chg,
+ states[di->charge_state],
+ di->chg_info.charger_type,
+ di->charge_status,
+ di->chg_info.conn_chg & AC_CHG,
+ di->chg_info.conn_chg & USB_CHG,
+ di->chg_info.online_chg & AC_CHG,
+ di->chg_info.online_chg & USB_CHG,
+ di->events.ac_cv_active,
+ di->events.usb_cv_active,
+ di->chg_info.ac_curr,
+ di->chg_info.usb_curr,
+ di->chg_info.ac_vset,
+ di->chg_info.ac_iset,
+ di->chg_info.usb_vset,
+ di->chg_info.usb_iset);
+
+ switch (di->charge_state) {
+ case STATE_HANDHELD_INIT:
+ abx500_chargalg_stop_charging(di);
+ di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ abx500_chargalg_state_to(di, STATE_HANDHELD);
+ /* Intentional fallthrough */
+
+ case STATE_HANDHELD:
+ break;
+
+ case STATE_SUSPENDED_INIT:
+ if (di->susp_status.ac_suspended)
+ abx500_chargalg_ac_en(di, false, 0, 0);
+ if (di->susp_status.usb_suspended)
+ abx500_chargalg_usb_en(di, false, 0, 0);
+ abx500_chargalg_stop_safety_timer(di);
+ abx500_chargalg_stop_maintenance_timer(di);
+ di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ di->maintenance_chg = false;
+ abx500_chargalg_state_to(di, STATE_SUSPENDED);
+ power_supply_changed(&di->chargalg_psy);
+ /* Intentional fallthrough */
+
+ case STATE_SUSPENDED:
+ /* CHARGING is suspended */
+ break;
+
+ case STATE_BATT_REMOVED_INIT:
+ abx500_chargalg_stop_charging(di);
+ abx500_chargalg_state_to(di, STATE_BATT_REMOVED);
+ /* Intentional fallthrough */
+
+ case STATE_BATT_REMOVED:
+ if (!di->events.batt_rem)
+ abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
+ case STATE_HW_TEMP_PROTECT_INIT:
+ abx500_chargalg_stop_charging(di);
+ abx500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT);
+ /* Intentional fallthrough */
+
+ case STATE_HW_TEMP_PROTECT:
+ if (!di->events.main_thermal_prot &&
+ !di->events.usb_thermal_prot)
+ abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
+ case STATE_OVV_PROTECT_INIT:
+ abx500_chargalg_stop_charging(di);
+ abx500_chargalg_state_to(di, STATE_OVV_PROTECT);
+ /* Intentional fallthrough */
+
+ case STATE_OVV_PROTECT:
+ if (!di->events.vbus_ovv &&
+ !di->events.main_ovv &&
+ !di->events.batt_ovv &&
+ di->chg_info.usb_chg_ok &&
+ di->chg_info.ac_chg_ok)
+ abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
+ case STATE_CHG_NOT_OK_INIT:
+ abx500_chargalg_stop_charging(di);
+ abx500_chargalg_state_to(di, STATE_CHG_NOT_OK);
+ /* Intentional fallthrough */
+
+ case STATE_CHG_NOT_OK:
+ if (!di->events.mainextchnotok &&
+ !di->events.usbchargernotok)
+ abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
+ case STATE_SAFETY_TIMER_EXPIRED_INIT:
+ abx500_chargalg_stop_charging(di);
+ abx500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED);
+ /* Intentional fallthrough */
+
+ case STATE_SAFETY_TIMER_EXPIRED:
+ /* We exit this state when charger is removed */
+ break;
+
+ case STATE_NORMAL_INIT:
+ abx500_chargalg_start_charging(di,
+ di->bat->bat_type[di->bat->batt_id].normal_vol_lvl,
+ di->bat->bat_type[di->bat->batt_id].normal_cur_lvl);
+ abx500_chargalg_state_to(di, STATE_NORMAL);
+ abx500_chargalg_start_safety_timer(di);
+ abx500_chargalg_stop_maintenance_timer(di);
+ init_maxim_chg_curr(di);
+ di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+ di->eoc_cnt = 0;
+ di->maintenance_chg = false;
+ power_supply_changed(&di->chargalg_psy);
+
+ break;
+
+ case STATE_NORMAL:
+ handle_maxim_chg_curr(di);
+ if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
+ di->maintenance_chg) {
+ if (di->bat->no_maintenance)
+ abx500_chargalg_state_to(di,
+ STATE_WAIT_FOR_RECHARGE_INIT);
+ else
+ abx500_chargalg_state_to(di,
+ STATE_MAINTENANCE_A_INIT);
+ }
+ break;
+
+ /* This state will be used when the maintenance state is disabled */
+ case STATE_WAIT_FOR_RECHARGE_INIT:
+ abx500_chargalg_hold_charging(di);
+ abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
+ di->rch_cnt = RCH_COND_CNT;
+ /* Intentional fallthrough */
+
+ case STATE_WAIT_FOR_RECHARGE:
+ if (di->batt_data.volt <=
+ di->bat->bat_type[di->bat->batt_id].recharge_vol) {
+ if (di->rch_cnt-- == 0)
+ abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ } else
+ di->rch_cnt = RCH_COND_CNT;
+ break;
+
+ case STATE_MAINTENANCE_A_INIT:
+ abx500_chargalg_stop_safety_timer(di);
+ abx500_chargalg_start_maintenance_timer(di,
+ di->bat->bat_type[
+ di->bat->batt_id].maint_a_chg_timer_h);
+ abx500_chargalg_start_charging(di,
+ di->bat->bat_type[
+ di->bat->batt_id].maint_a_vol_lvl,
+ di->bat->bat_type[
+ di->bat->batt_id].maint_a_cur_lvl);
+ abx500_chargalg_state_to(di, STATE_MAINTENANCE_A);
+ power_supply_changed(&di->chargalg_psy);
+ /* Intentional fallthrough*/
+
+ case STATE_MAINTENANCE_A:
+ if (di->events.maintenance_timer_expired) {
+ abx500_chargalg_stop_maintenance_timer(di);
+ abx500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT);
+ }
+ break;
+
+ case STATE_MAINTENANCE_B_INIT:
+ abx500_chargalg_start_maintenance_timer(di,
+ di->bat->bat_type[
+ di->bat->batt_id].maint_b_chg_timer_h);
+ abx500_chargalg_start_charging(di,
+ di->bat->bat_type[
+ di->bat->batt_id].maint_b_vol_lvl,
+ di->bat->bat_type[
+ di->bat->batt_id].maint_b_cur_lvl);
+ abx500_chargalg_state_to(di, STATE_MAINTENANCE_B);
+ power_supply_changed(&di->chargalg_psy);
+ /* Intentional fallthrough*/
+
+ case STATE_MAINTENANCE_B:
+ if (di->events.maintenance_timer_expired) {
+ abx500_chargalg_stop_maintenance_timer(di);
+ abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ }
+ break;
+
+ case STATE_TEMP_LOWHIGH_INIT:
+ abx500_chargalg_start_charging(di,
+ di->bat->bat_type[
+ di->bat->batt_id].low_high_vol_lvl,
+ di->bat->bat_type[
+ di->bat->batt_id].low_high_cur_lvl);
+ abx500_chargalg_stop_maintenance_timer(di);
+ di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+ abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
+ power_supply_changed(&di->chargalg_psy);
+ /* Intentional fallthrough */
+
+ case STATE_TEMP_LOWHIGH:
+ if (!di->events.btemp_lowhigh)
+ abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
+ case STATE_WD_EXPIRED_INIT:
+ abx500_chargalg_stop_charging(di);
+ abx500_chargalg_state_to(di, STATE_WD_EXPIRED);
+ /* Intentional fallthrough */
+
+ case STATE_WD_EXPIRED:
+ if (!di->events.ac_wd_expired &&
+ !di->events.usb_wd_expired)
+ abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
+ case STATE_TEMP_UNDEROVER_INIT:
+ abx500_chargalg_stop_charging(di);
+ abx500_chargalg_state_to(di, STATE_TEMP_UNDEROVER);
+ /* Intentional fallthrough */
+
+ case STATE_TEMP_UNDEROVER:
+ if (!di->events.btemp_underover)
+ abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+ }
+
+ /* Start charging directly if the new state is a charge state */
+ if (di->charge_state == STATE_NORMAL_INIT ||
+ di->charge_state == STATE_MAINTENANCE_A_INIT ||
+ di->charge_state == STATE_MAINTENANCE_B_INIT)
+ queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * abx500_chargalg_periodic_work() - Periodic work for the algorithm
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for the charging algorithm
+ */
+static void abx500_chargalg_periodic_work(struct work_struct *work)
+{
+ struct abx500_chargalg *di = container_of(work,
+ struct abx500_chargalg, chargalg_periodic_work.work);
+
+ abx500_chargalg_algorithm(di);
+
+ /*
+ * If a charger is connected then the battery has to be monitored
+ * frequently, else the work can be delayed.
+ */
+ if (di->chg_info.conn_chg)
+ queue_delayed_work(di->chargalg_wq,
+ &di->chargalg_periodic_work,
+ di->bat->interval_charging * HZ);
+ else
+ queue_delayed_work(di->chargalg_wq,
+ &di->chargalg_periodic_work,
+ di->bat->interval_not_charging * HZ);
+}
+
+/**
+ * abx500_chargalg_wd_work() - periodic work to kick the charger watchdog
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for kicking the charger watchdog
+ */
+static void abx500_chargalg_wd_work(struct work_struct *work)
+{
+ int ret;
+ struct abx500_chargalg *di = container_of(work,
+ struct abx500_chargalg, chargalg_wd_work.work);
+
+ dev_dbg(di->dev, "abx500_chargalg_wd_work\n");
+
+ ret = abx500_chargalg_kick_watchdog(di);
+ if (ret < 0)
+ dev_err(di->dev, "failed to kick watchdog\n");
+
+ queue_delayed_work(di->chargalg_wq,
+ &di->chargalg_wd_work, CHG_WD_INTERVAL);
+}
+
+/**
+ * abx500_chargalg_work() - Work to run the charging algorithm instantly
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for calling the charging algorithm
+ */
+static void abx500_chargalg_work(struct work_struct *work)
+{
+ struct abx500_chargalg *di = container_of(work,
+ struct abx500_chargalg, chargalg_work);
+
+ abx500_chargalg_algorithm(di);
+}
+
+/**
+ * abx500_chargalg_get_property() - get the chargalg properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the
+ * chargalg properties by reading the sysfs files.
+ * status: charging/discharging/full/unknown
+ * health: health of the battery
+ * Returns error code in case of failure else 0 on success
+ */
+static int abx500_chargalg_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct abx500_chargalg *di;
+
+ di = to_abx500_chargalg_device_info(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = di->charge_status;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (di->events.batt_ovv) {
+ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ } else if (di->events.btemp_underover) {
+ if (di->batt_data.temp <= di->bat->temp_under)
+ val->intval = POWER_SUPPLY_HEALTH_COLD;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ } else {
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Exposure to the sysfs interface */
+
+/**
+ * abx500_chargalg_sysfs_charger() - sysfs store operations
+ * @kobj: pointer to the struct kobject
+ * @attr: pointer to the struct attribute
+ * @buf: buffer that holds the parameter passed from userspace
+ * @length: length of the parameter passed
+ *
+ * Returns length of the buffer(input taken from user space) on success
+ * else error code on failure
+ * The operation to be performed on passing the parameters from the user space.
+ */
+static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t length)
+{
+ struct abx500_chargalg *di = container_of(kobj,
+ struct abx500_chargalg, chargalg_kobject);
+ long int param;
+ int ac_usb;
+ int ret;
+ char entry = *attr->name;
+
+ switch (entry) {
+ case 'c':
+ ret = strict_strtol(buf, 10, &param);
+ if (ret < 0)
+ return ret;
+
+ ac_usb = param;
+ switch (ac_usb) {
+ case 0:
+ /* Disable charging */
+ di->susp_status.ac_suspended = true;
+ di->susp_status.usb_suspended = true;
+ di->susp_status.suspended_change = true;
+ /* Trigger a state change */
+ queue_work(di->chargalg_wq,
+ &di->chargalg_work);
+ break;
+ case 1:
+ /* Enable AC Charging */
+ di->susp_status.ac_suspended = false;
+ di->susp_status.suspended_change = true;
+ /* Trigger a state change */
+ queue_work(di->chargalg_wq,
+ &di->chargalg_work);
+ break;
+ case 2:
+ /* Enable USB charging */
+ di->susp_status.usb_suspended = false;
+ di->susp_status.suspended_change = true;
+ /* Trigger a state change */
+ queue_work(di->chargalg_wq,
+ &di->chargalg_work);
+ break;
+ default:
+ dev_info(di->dev, "Wrong input\n"
+ "Enter 0. Disable AC/USB Charging\n"
+ "1. Enable AC charging\n"
+ "2. Enable USB Charging\n");
+ };
+ break;
+ };
+ return strlen(buf);
+}
+
+static struct attribute abx500_chargalg_en_charger = \
+{
+ .name = "chargalg",
+ .mode = S_IWUGO,
+};
+
+static struct attribute *abx500_chargalg_chg[] = {
+ &abx500_chargalg_en_charger,
+ NULL
+};
+
+static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
+ .store = abx500_chargalg_sysfs_charger,
+};
+
+static struct kobj_type abx500_chargalg_ktype = {
+ .sysfs_ops = &abx500_chargalg_sysfs_ops,
+ .default_attrs = abx500_chargalg_chg,
+};
+
+/**
+ * abx500_chargalg_sysfs_exit() - de-init of sysfs entry
+ * @di: pointer to the struct abx500_chargalg
+ *
+ * This function removes the entry in sysfs.
+ */
+static void abx500_chargalg_sysfs_exit(struct abx500_chargalg *di)
+{
+ kobject_del(&di->chargalg_kobject);
+}
+
+/**
+ * abx500_chargalg_sysfs_init() - init of sysfs entry
+ * @di: pointer to the struct abx500_chargalg
+ *
+ * This function adds an entry in sysfs.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int abx500_chargalg_sysfs_init(struct abx500_chargalg *di)
+{
+ int ret = 0;
+
+ ret = kobject_init_and_add(&di->chargalg_kobject,
+ &abx500_chargalg_ktype,
+ NULL, "abx500_chargalg");
+ if (ret < 0)
+ dev_err(di->dev, "failed to create sysfs entry\n");
+
+ return ret;
+}
+/* Exposure to the sysfs interface <<END>> */
+
+#if defined(CONFIG_PM)
+static int abx500_chargalg_resume(struct platform_device *pdev)
+{
+ struct abx500_chargalg *di = platform_get_drvdata(pdev);
+
+ /* Kick charger watchdog if charging (any charger online) */
+ if (di->chg_info.online_chg)
+ queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0);
+
+ /*
+ * Run the charging algorithm directly to be sure we don't
+ * do it too seldom
+ */
+ queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
+
+ return 0;
+}
+
+static int abx500_chargalg_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct abx500_chargalg *di = platform_get_drvdata(pdev);
+
+ if (di->chg_info.online_chg)
+ cancel_delayed_work_sync(&di->chargalg_wd_work);
+
+ cancel_delayed_work_sync(&di->chargalg_periodic_work);
+
+ return 0;
+}
+#else
+#define abx500_chargalg_suspend NULL
+#define abx500_chargalg_resume NULL
+#endif
+
+static int __devexit abx500_chargalg_remove(struct platform_device *pdev)
+{
+ struct abx500_chargalg *di = platform_get_drvdata(pdev);
+
+ /* sysfs interface to enable/disbale charging from user space */
+ abx500_chargalg_sysfs_exit(di);
+
+ /* Delete the work queue */
+ destroy_workqueue(di->chargalg_wq);
+
+ flush_scheduled_work();
+ power_supply_unregister(&di->chargalg_psy);
+ platform_set_drvdata(pdev, NULL);
+ kfree(di);
+
+ return 0;
+}
+
+static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
+{
+ struct abx500_bm_plat_data *plat_data;
+ int ret = 0;
+
+ struct abx500_chargalg *di =
+ kzalloc(sizeof(struct abx500_chargalg), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ /* get device struct */
+ di->dev = &pdev->dev;
+
+ plat_data = pdev->dev.platform_data;
+ di->pdata = plat_data->chargalg;
+ di->bat = plat_data->battery;
+
+ /* chargalg supply */
+ di->chargalg_psy.name = "abx500_chargalg";
+ di->chargalg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->chargalg_psy.properties = abx500_chargalg_props;
+ di->chargalg_psy.num_properties = ARRAY_SIZE(abx500_chargalg_props);
+ di->chargalg_psy.get_property = abx500_chargalg_get_property;
+ di->chargalg_psy.supplied_to = di->pdata->supplied_to;
+ di->chargalg_psy.num_supplicants = di->pdata->num_supplicants;
+ di->chargalg_psy.external_power_changed =
+ abx500_chargalg_external_power_changed;
+
+ /* Initilialize safety timer */
+ init_timer(&di->safety_timer);
+ di->safety_timer.function = abx500_chargalg_safety_timer_expired;
+ di->safety_timer.data = (unsigned long) di;
+
+ /* Initilialize maintenance timer */
+ init_timer(&di->maintenance_timer);
+ di->maintenance_timer.function =
+ abx500_chargalg_maintenance_timer_expired;
+ di->maintenance_timer.data = (unsigned long) di;
+
+ /* Create a work queue for the chargalg */
+ di->chargalg_wq =
+ create_singlethread_workqueue("abx500_chargalg_wq");
+ if (di->chargalg_wq == NULL) {
+ dev_err(di->dev, "failed to create work queue\n");
+ goto free_device_info;
+ }
+
+ /* Init work for chargalg */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->chargalg_periodic_work,
+ abx500_chargalg_periodic_work);
+ INIT_DELAYED_WORK_DEFERRABLE(&di->chargalg_wd_work,
+ abx500_chargalg_wd_work);
+
+ /* Init work for chargalg */
+ INIT_WORK(&di->chargalg_work, abx500_chargalg_work);
+
+ /* To detect charger at startup */
+ di->chg_info.prev_conn_chg = -1;
+
+ /* Register chargalg power supply class */
+ ret = power_supply_register(di->dev, &di->chargalg_psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register chargalg psy\n");
+ goto free_chargalg_wq;
+ }
+
+ platform_set_drvdata(pdev, di);
+
+ /* sysfs interface to enable/disable charging from user space */
+ ret = abx500_chargalg_sysfs_init(di);
+ if (ret) {
+ dev_err(di->dev, "failed to create sysfs entry\n");
+ goto free_psy;
+ }
+
+ /* Run the charging algorithm */
+ queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
+
+ dev_info(di->dev, "probe success\n");
+ return ret;
+
+free_psy:
+ power_supply_unregister(&di->chargalg_psy);
+free_chargalg_wq:
+ destroy_workqueue(di->chargalg_wq);
+free_device_info:
+ kfree(di);
+
+ return ret;
+}
+
+static struct platform_driver abx500_chargalg_driver = {
+ .probe = abx500_chargalg_probe,
+ .remove = __devexit_p(abx500_chargalg_remove),
+ .suspend = abx500_chargalg_suspend,
+ .resume = abx500_chargalg_resume,
+ .driver = {
+ .name = "abx500-chargalg",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init abx500_chargalg_init(void)
+{
+ return platform_driver_register(&abx500_chargalg_driver);
+}
+
+static void __exit abx500_chargalg_exit(void)
+{
+ platform_driver_unregister(&abx500_chargalg_driver);
+}
+
+module_init(abx500_chargalg_init);
+module_exit(abx500_chargalg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
+MODULE_ALIAS("platform:abx500-chargalg");
+MODULE_DESCRIPTION("abx500 battery charging algorithm");
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
index 88fd971..9eca9f1 100644
--- a/drivers/power/charger-manager.c
+++ b/drivers/power/charger-manager.c
@@ -134,12 +134,11 @@ static int get_batt_uV(struct charger_manager *cm, int *uV)
union power_supply_propval val;
int ret;
- if (cm->fuel_gauge)
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
- POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
- else
+ if (!cm->fuel_gauge)
return -ENODEV;
+ ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
if (ret)
return ret;
@@ -245,9 +244,7 @@ static int try_charger_enable(struct charger_manager *cm, bool enable)
struct charger_desc *desc = cm->desc;
/* Ignore if it's redundent command */
- if (enable && cm->charger_enabled)
- return 0;
- if (!enable && !cm->charger_enabled)
+ if (enable == cm->charger_enabled)
return 0;
if (enable) {
@@ -309,9 +306,7 @@ static void uevent_notify(struct charger_manager *cm, const char *event)
if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE))
return; /* Duplicated. */
- else
- strncpy(env_str_save, event, UEVENT_BUF_SIZE);
-
+ strncpy(env_str_save, event, UEVENT_BUF_SIZE);
return;
}
@@ -387,8 +382,10 @@ static bool cm_monitor(void)
mutex_lock(&cm_list_mtx);
- list_for_each_entry(cm, &cm_list, entry)
- stop = stop || _cm_monitor(cm);
+ list_for_each_entry(cm, &cm_list, entry) {
+ if (_cm_monitor(cm))
+ stop = true;
+ }
mutex_unlock(&cm_list_mtx);
@@ -402,7 +399,8 @@ static int charger_get_property(struct power_supply *psy,
struct charger_manager *cm = container_of(psy,
struct charger_manager, charger_psy);
struct charger_desc *desc = cm->desc;
- int i, ret = 0, uV;
+ int ret = 0;
+ int uV;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
@@ -428,8 +426,7 @@ static int charger_get_property(struct power_supply *psy,
val->intval = 0;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- ret = get_batt_uV(cm, &i);
- val->intval = i;
+ ret = get_batt_uV(cm, &val->intval);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
@@ -697,8 +694,10 @@ bool cm_suspend_again(void)
mutex_lock(&cm_list_mtx);
list_for_each_entry(cm, &cm_list, entry) {
if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
- cm->status_save_batt != is_batt_present(cm))
+ cm->status_save_batt != is_batt_present(cm)) {
ret = false;
+ break;
+ }
}
mutex_unlock(&cm_list_mtx);
@@ -855,11 +854,10 @@ static int charger_manager_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, cm);
- memcpy(&cm->charger_psy, &psy_default,
- sizeof(psy_default));
+ memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default));
+
if (!desc->psy_name) {
- strncpy(cm->psy_name_buf, psy_default.name,
- PSY_NAME_MAX);
+ strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX);
} else {
strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
}
@@ -894,15 +892,15 @@ static int charger_manager_probe(struct platform_device *pdev)
POWER_SUPPLY_PROP_CURRENT_NOW;
cm->charger_psy.num_properties++;
}
- if (!desc->measure_battery_temp) {
- cm->charger_psy.properties[cm->charger_psy.num_properties] =
- POWER_SUPPLY_PROP_TEMP_AMBIENT;
- cm->charger_psy.num_properties++;
- }
+
if (desc->measure_battery_temp) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_TEMP;
cm->charger_psy.num_properties++;
+ } else {
+ cm->charger_psy.properties[cm->charger_psy.num_properties] =
+ POWER_SUPPLY_PROP_TEMP_AMBIENT;
+ cm->charger_psy.num_properties++;
}
ret = power_supply_register(NULL, &cm->charger_psy);
@@ -933,9 +931,8 @@ static int charger_manager_probe(struct platform_device *pdev)
return 0;
err_chg_enable:
- if (desc->charger_regulators)
- regulator_bulk_free(desc->num_charger_regulators,
- desc->charger_regulators);
+ regulator_bulk_free(desc->num_charger_regulators,
+ desc->charger_regulators);
err_bulk_get:
power_supply_unregister(&cm->charger_psy);
err_register:
@@ -961,10 +958,8 @@ static int __devexit charger_manager_remove(struct platform_device *pdev)
list_del(&cm->entry);
mutex_unlock(&cm_list_mtx);
- if (desc->charger_regulators)
- regulator_bulk_free(desc->num_charger_regulators,
- desc->charger_regulators);
-
+ regulator_bulk_free(desc->num_charger_regulators,
+ desc->charger_regulators);
power_supply_unregister(&cm->charger_psy);
kfree(cm->charger_psy.properties);
kfree(cm->charger_stat);
@@ -982,9 +977,7 @@ MODULE_DEVICE_TABLE(platform, charger_manager_id);
static int cm_suspend_prepare(struct device *dev)
{
- struct platform_device *pdev = container_of(dev, struct platform_device,
- dev);
- struct charger_manager *cm = platform_get_drvdata(pdev);
+ struct charger_manager *cm = dev_get_drvdata(dev);
if (!cm_suspended) {
if (rtc_dev) {
@@ -1020,9 +1013,7 @@ static int cm_suspend_prepare(struct device *dev)
static void cm_suspend_complete(struct device *dev)
{
- struct platform_device *pdev = container_of(dev, struct platform_device,
- dev);
- struct charger_manager *cm = platform_get_drvdata(pdev);
+ struct charger_manager *cm = dev_get_drvdata(dev);
if (cm_suspended) {
if (rtc_dev) {
diff --git a/drivers/power/da9052-battery.c b/drivers/power/da9052-battery.c
index e8ea47a..a5f6a0e 100644
--- a/drivers/power/da9052-battery.c
+++ b/drivers/power/da9052-battery.c
@@ -612,6 +612,7 @@ static s32 __devinit da9052_bat_probe(struct platform_device *pdev)
if (ret)
goto err;
+ platform_set_drvdata(pdev, bat);
return 0;
err:
@@ -633,6 +634,7 @@ static int __devexit da9052_bat_remove(struct platform_device *pdev)
free_irq(bat->da9052->irq_base + irq, bat);
}
power_supply_unregister(&bat->psy);
+ kfree(bat);
return 0;
}
@@ -645,18 +647,7 @@ static struct platform_driver da9052_bat_driver = {
.owner = THIS_MODULE,
},
};
-
-static int __init da9052_bat_init(void)
-{
- return platform_driver_register(&da9052_bat_driver);
-}
-module_init(da9052_bat_init);
-
-static void __exit da9052_bat_exit(void)
-{
- platform_driver_unregister(&da9052_bat_driver);
-}
-module_exit(da9052_bat_exit);
+module_platform_driver(da9052_bat_driver);
MODULE_DESCRIPTION("DA9052 BAT Device Driver");
MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c
index bfbce5d..6bb6e2f 100644
--- a/drivers/power/ds2782_battery.c
+++ b/drivers/power/ds2782_battery.c
@@ -403,18 +403,7 @@ static struct i2c_driver ds278x_battery_driver = {
.remove = ds278x_battery_remove,
.id_table = ds278x_id,
};
-
-static int __init ds278x_init(void)
-{
- return i2c_add_driver(&ds278x_battery_driver);
-}
-module_init(ds278x_init);
-
-static void __exit ds278x_exit(void)
-{
- i2c_del_driver(&ds278x_battery_driver);
-}
-module_exit(ds278x_exit);
+module_i2c_driver(ds278x_battery_driver);
MODULE_AUTHOR("Ryan Mallon");
MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c
index 1289a5f..39eb50f 100644
--- a/drivers/power/isp1704_charger.c
+++ b/drivers/power/isp1704_charger.c
@@ -480,6 +480,7 @@ fail0:
dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
+ isp1704_charger_set_power(isp, 0);
return ret;
}
diff --git a/drivers/power/lp8727_charger.c b/drivers/power/lp8727_charger.c
index c53dd12..d8b7578 100644
--- a/drivers/power/lp8727_charger.c
+++ b/drivers/power/lp8727_charger.c
@@ -1,6 +1,7 @@
/*
- * Driver for LP8727 Micro/Mini USB IC with intergrated charger
+ * Driver for LP8727 Micro/Mini USB IC with integrated charger
*
+ * Copyright (C) 2011 Texas Instruments
* Copyright (C) 2011 National Semiconductor
*
* This program is free software; you can redistribute it and/or modify
@@ -25,7 +26,7 @@
#define INT1 0x4
#define INT2 0x5
#define STATUS1 0x6
-#define STATUS2 0x7
+#define STATUS2 0x7
#define CHGCTRL2 0x9
/* CTRL1 register */
@@ -91,7 +92,7 @@ struct lp8727_chg {
enum lp8727_dev_id devid;
};
-static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
+static int lp8727_read_bytes(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
{
s32 ret;
@@ -102,29 +103,22 @@ static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
return (ret != len) ? -EIO : 0;
}
-static int lp8727_i2c_write(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
+static inline int lp8727_read_byte(struct lp8727_chg *pchg, u8 reg, u8 *data)
{
- s32 ret;
+ return lp8727_read_bytes(pchg, reg, data, 1);
+}
+
+static int lp8727_write_byte(struct lp8727_chg *pchg, u8 reg, u8 data)
+{
+ int ret;
mutex_lock(&pchg->xfer_lock);
- ret = i2c_smbus_write_i2c_block_data(pchg->client, reg, len, data);
+ ret = i2c_smbus_write_byte_data(pchg->client, reg, data);
mutex_unlock(&pchg->xfer_lock);
return ret;
}
-static inline int lp8727_i2c_read_byte(struct lp8727_chg *pchg, u8 reg,
- u8 *data)
-{
- return lp8727_i2c_read(pchg, reg, data, 1);
-}
-
-static inline int lp8727_i2c_write_byte(struct lp8727_chg *pchg, u8 reg,
- u8 *data)
-{
- return lp8727_i2c_write(pchg, reg, data, 1);
-}
-
static int lp8727_is_charger_attached(const char *name, int id)
{
if (name) {
@@ -137,37 +131,41 @@ static int lp8727_is_charger_attached(const char *name, int id)
return (id >= ID_TA && id <= ID_USB_CHG) ? 1 : 0;
}
-static void lp8727_init_device(struct lp8727_chg *pchg)
+static int lp8727_init_device(struct lp8727_chg *pchg)
{
u8 val;
+ int ret;
val = ID200_EN | ADC_EN | CP_EN;
- if (lp8727_i2c_write_byte(pchg, CTRL1, &val))
- dev_err(pchg->dev, "i2c write err : addr=0x%.2x\n", CTRL1);
+ ret = lp8727_write_byte(pchg, CTRL1, val);
+ if (ret)
+ return ret;
val = INT_EN | CHGDET_EN;
- if (lp8727_i2c_write_byte(pchg, CTRL2, &val))
- dev_err(pchg->dev, "i2c write err : addr=0x%.2x\n", CTRL2);
+ ret = lp8727_write_byte(pchg, CTRL2, val);
+ if (ret)
+ return ret;
+
+ return 0;
}
static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg)
{
u8 val;
- lp8727_i2c_read_byte(pchg, STATUS1, &val);
- return (val & DCPORT);
+ lp8727_read_byte(pchg, STATUS1, &val);
+ return val & DCPORT;
}
static int lp8727_is_usb_charger(struct lp8727_chg *pchg)
{
u8 val;
- lp8727_i2c_read_byte(pchg, STATUS1, &val);
- return (val & CHPORT);
+ lp8727_read_byte(pchg, STATUS1, &val);
+ return val & CHPORT;
}
static void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw)
{
- u8 val = sw;
- lp8727_i2c_write_byte(pchg, SWCTRL, &val);
+ lp8727_write_byte(pchg, SWCTRL, sw);
}
static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin)
@@ -207,9 +205,9 @@ static void lp8727_enable_chgdet(struct lp8727_chg *pchg)
{
u8 val;
- lp8727_i2c_read_byte(pchg, CTRL2, &val);
+ lp8727_read_byte(pchg, CTRL2, &val);
val |= CHGDET_EN;
- lp8727_i2c_write_byte(pchg, CTRL2, &val);
+ lp8727_write_byte(pchg, CTRL2, val);
}
static void lp8727_delayed_func(struct work_struct *_work)
@@ -218,7 +216,7 @@ static void lp8727_delayed_func(struct work_struct *_work)
struct lp8727_chg *pchg =
container_of(_work, struct lp8727_chg, work.work);
- if (lp8727_i2c_read(pchg, INT1, intstat, 2)) {
+ if (lp8727_read_bytes(pchg, INT1, intstat, 2)) {
dev_err(pchg->dev, "can not read INT registers\n");
return;
}
@@ -244,20 +242,22 @@ static irqreturn_t lp8727_isr_func(int irq, void *ptr)
return IRQ_HANDLED;
}
-static void lp8727_intr_config(struct lp8727_chg *pchg)
+static int lp8727_intr_config(struct lp8727_chg *pchg)
{
INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func);
pchg->irqthread = create_singlethread_workqueue("lp8727-irqthd");
- if (!pchg->irqthread)
+ if (!pchg->irqthread) {
dev_err(pchg->dev, "can not create thread for lp8727\n");
-
- if (request_threaded_irq(pchg->client->irq,
- NULL,
- lp8727_isr_func,
- IRQF_TRIGGER_FALLING, "lp8727_irq", pchg)) {
- dev_err(pchg->dev, "lp8727 irq can not be registered\n");
+ return -ENOMEM;
}
+
+ return request_threaded_irq(pchg->client->irq,
+ NULL,
+ lp8727_isr_func,
+ IRQF_TRIGGER_FALLING,
+ "lp8727_irq",
+ pchg);
}
static enum power_supply_property lp8727_charger_prop[] = {
@@ -300,7 +300,7 @@ static int lp8727_battery_get_property(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (lp8727_is_charger_attached(psy->name, pchg->devid)) {
- lp8727_i2c_read_byte(pchg, STATUS1, &read);
+ lp8727_read_byte(pchg, STATUS1, &read);
if (((read & CHGSTAT) >> 4) == EOC)
val->intval = POWER_SUPPLY_STATUS_FULL;
else
@@ -310,7 +310,7 @@ static int lp8727_battery_get_property(struct power_supply *psy,
}
break;
case POWER_SUPPLY_PROP_HEALTH:
- lp8727_i2c_read_byte(pchg, STATUS2, &read);
+ lp8727_read_byte(pchg, STATUS2, &read);
read = (read & TEMP_STAT) >> 5;
if (read >= 0x1 && read <= 0x3)
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
@@ -351,7 +351,7 @@ static void lp8727_charger_changed(struct power_supply *psy)
eoc_level = pchg->chg_parm->eoc_level;
ichg = pchg->chg_parm->ichg;
val = (ichg << 4) | eoc_level;
- lp8727_i2c_write_byte(pchg, CHGCTRL2, &val);
+ lp8727_write_byte(pchg, CHGCTRL2, val);
}
}
}
@@ -439,15 +439,29 @@ static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
mutex_init(&pchg->xfer_lock);
- lp8727_init_device(pchg);
- lp8727_intr_config(pchg);
+ ret = lp8727_init_device(pchg);
+ if (ret) {
+ dev_err(pchg->dev, "i2c communication err: %d", ret);
+ goto error;
+ }
+
+ ret = lp8727_intr_config(pchg);
+ if (ret) {
+ dev_err(pchg->dev, "irq handler err: %d", ret);
+ goto error;
+ }
ret = lp8727_register_psy(pchg);
- if (ret)
- dev_err(pchg->dev,
- "can not register power supplies. err=%d", ret);
+ if (ret) {
+ dev_err(pchg->dev, "power supplies register err: %d", ret);
+ goto error;
+ }
return 0;
+
+error:
+ kfree(pchg);
+ return ret;
}
static int __devexit lp8727_remove(struct i2c_client *cl)
@@ -466,6 +480,7 @@ static const struct i2c_device_id lp8727_ids[] = {
{"lp8727", 0},
{ }
};
+MODULE_DEVICE_TABLE(i2c, lp8727_ids);
static struct i2c_driver lp8727_driver = {
.driver = {
@@ -475,21 +490,9 @@ static struct i2c_driver lp8727_driver = {
.remove = __devexit_p(lp8727_remove),
.id_table = lp8727_ids,
};
+module_i2c_driver(lp8727_driver);
-static int __init lp8727_init(void)
-{
- return i2c_add_driver(&lp8727_driver);
-}
-
-static void __exit lp8727_exit(void)
-{
- i2c_del_driver(&lp8727_driver);
-}
-
-module_init(lp8727_init);
-module_exit(lp8727_exit);
-
-MODULE_DESCRIPTION("National Semiconductor LP8727 charger driver");
-MODULE_AUTHOR
- ("Woogyom Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>");
+MODULE_DESCRIPTION("TI/National Semiconductor LP8727 charger driver");
+MODULE_AUTHOR("Woogyom Kim <milo.kim@ti.com>, "
+ "Daniel Jeong <daniel.jeong@ti.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/power/max17040_battery.c b/drivers/power/max17040_battery.c
index 2f2f9a6..c284143 100644
--- a/drivers/power/max17040_battery.c
+++ b/drivers/power/max17040_battery.c
@@ -290,18 +290,7 @@ static struct i2c_driver max17040_i2c_driver = {
.resume = max17040_resume,
.id_table = max17040_id,
};
-
-static int __init max17040_init(void)
-{
- return i2c_add_driver(&max17040_i2c_driver);
-}
-module_init(max17040_init);
-
-static void __exit max17040_exit(void)
-{
- i2c_del_driver(&max17040_i2c_driver);
-}
-module_exit(max17040_exit);
+module_i2c_driver(max17040_i2c_driver);
MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
MODULE_DESCRIPTION("MAX17040 Fuel Gauge");
diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c
index 86acee2..04620c2 100644
--- a/drivers/power/max17042_battery.c
+++ b/drivers/power/max17042_battery.c
@@ -26,14 +26,47 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
#include <linux/power_supply.h>
#include <linux/power/max17042_battery.h>
+#include <linux/of.h>
+
+/* Status register bits */
+#define STATUS_POR_BIT (1 << 1)
+#define STATUS_BST_BIT (1 << 3)
+#define STATUS_VMN_BIT (1 << 8)
+#define STATUS_TMN_BIT (1 << 9)
+#define STATUS_SMN_BIT (1 << 10)
+#define STATUS_BI_BIT (1 << 11)
+#define STATUS_VMX_BIT (1 << 12)
+#define STATUS_TMX_BIT (1 << 13)
+#define STATUS_SMX_BIT (1 << 14)
+#define STATUS_BR_BIT (1 << 15)
+
+/* Interrupt mask bits */
+#define CONFIG_ALRT_BIT_ENBL (1 << 2)
+#define STATUS_INTR_SOCMIN_BIT (1 << 10)
+#define STATUS_INTR_SOCMAX_BIT (1 << 14)
+
+#define VFSOC0_LOCK 0x0000
+#define VFSOC0_UNLOCK 0x0080
+#define MODEL_UNLOCK1 0X0059
+#define MODEL_UNLOCK2 0X00C4
+#define MODEL_LOCK1 0X0000
+#define MODEL_LOCK2 0X0000
+
+#define dQ_ACC_DIV 0x4
+#define dP_ACC_100 0x1900
+#define dP_ACC_200 0x3200
struct max17042_chip {
struct i2c_client *client;
struct power_supply battery;
struct max17042_platform_data *pdata;
+ struct work_struct work;
+ int init_complete;
};
static int max17042_write_reg(struct i2c_client *client, u8 reg, u16 value)
@@ -87,6 +120,9 @@ static int max17042_get_property(struct power_supply *psy,
struct max17042_chip, battery);
int ret;
+ if (!chip->init_complete)
+ return -EAGAIN;
+
switch (psp) {
case POWER_SUPPLY_PROP_PRESENT:
ret = max17042_read_reg(chip->client, MAX17042_STATUS);
@@ -136,21 +172,18 @@ static int max17042_get_property(struct power_supply *psy,
val->intval = ret * 625 / 8;
break;
case POWER_SUPPLY_PROP_CAPACITY:
- ret = max17042_read_reg(chip->client, MAX17042_SOC);
+ ret = max17042_read_reg(chip->client, MAX17042_RepSOC);
if (ret < 0)
return ret;
val->intval = ret >> 8;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
- ret = max17042_read_reg(chip->client, MAX17042_RepSOC);
+ ret = max17042_read_reg(chip->client, MAX17042_FullCAP);
if (ret < 0)
return ret;
- if ((ret >> 8) >= MAX17042_BATTERY_FULL)
- val->intval = 1;
- else if (ret >= 0)
- val->intval = 0;
+ val->intval = ret * 1000 / 2;
break;
case POWER_SUPPLY_PROP_TEMP:
ret = max17042_read_reg(chip->client, MAX17042_TEMP);
@@ -210,22 +243,419 @@ static int max17042_get_property(struct power_supply *psy,
return 0;
}
+static int max17042_write_verify_reg(struct i2c_client *client,
+ u8 reg, u16 value)
+{
+ int retries = 8;
+ int ret;
+ u16 read_value;
+
+ do {
+ ret = i2c_smbus_write_word_data(client, reg, value);
+ read_value = max17042_read_reg(client, reg);
+ if (read_value != value) {
+ ret = -EIO;
+ retries--;
+ }
+ } while (retries && read_value != value);
+
+ if (ret < 0)
+ dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+ return ret;
+}
+
+static inline void max17042_override_por(
+ struct i2c_client *client, u8 reg, u16 value)
+{
+ if (value)
+ max17042_write_reg(client, reg, value);
+}
+
+static inline void max10742_unlock_model(struct max17042_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_UNLOCK1);
+ max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_UNLOCK2);
+}
+
+static inline void max10742_lock_model(struct max17042_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_LOCK1);
+ max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_LOCK2);
+}
+
+static inline void max17042_write_model_data(struct max17042_chip *chip,
+ u8 addr, int size)
+{
+ struct i2c_client *client = chip->client;
+ int i;
+ for (i = 0; i < size; i++)
+ max17042_write_reg(client, addr + i,
+ chip->pdata->config_data->cell_char_tbl[i]);
+}
+
+static inline void max17042_read_model_data(struct max17042_chip *chip,
+ u8 addr, u16 *data, int size)
+{
+ struct i2c_client *client = chip->client;
+ int i;
+
+ for (i = 0; i < size; i++)
+ data[i] = max17042_read_reg(client, addr + i);
+}
+
+static inline int max17042_model_data_compare(struct max17042_chip *chip,
+ u16 *data1, u16 *data2, int size)
+{
+ int i;
+
+ if (memcmp(data1, data2, size)) {
+ dev_err(&chip->client->dev, "%s compare failed\n", __func__);
+ for (i = 0; i < size; i++)
+ dev_info(&chip->client->dev, "0x%x, 0x%x",
+ data1[i], data2[i]);
+ dev_info(&chip->client->dev, "\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int max17042_init_model(struct max17042_chip *chip)
+{
+ int ret;
+ int table_size =
+ sizeof(chip->pdata->config_data->cell_char_tbl)/sizeof(u16);
+ u16 *temp_data;
+
+ temp_data = kzalloc(table_size, GFP_KERNEL);
+ if (!temp_data)
+ return -ENOMEM;
+
+ max10742_unlock_model(chip);
+ max17042_write_model_data(chip, MAX17042_MODELChrTbl,
+ table_size);
+ max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data,
+ table_size);
+
+ ret = max17042_model_data_compare(
+ chip,
+ chip->pdata->config_data->cell_char_tbl,
+ temp_data,
+ table_size);
+
+ max10742_lock_model(chip);
+ kfree(temp_data);
+
+ return ret;
+}
+
+static int max17042_verify_model_lock(struct max17042_chip *chip)
+{
+ int i;
+ int table_size =
+ sizeof(chip->pdata->config_data->cell_char_tbl);
+ u16 *temp_data;
+ int ret = 0;
+
+ temp_data = kzalloc(table_size, GFP_KERNEL);
+ if (!temp_data)
+ return -ENOMEM;
+
+ max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data,
+ table_size);
+ for (i = 0; i < table_size; i++)
+ if (temp_data[i])
+ ret = -EINVAL;
+
+ kfree(temp_data);
+ return ret;
+}
+
+static void max17042_write_config_regs(struct max17042_chip *chip)
+{
+ struct max17042_config_data *config = chip->pdata->config_data;
+
+ max17042_write_reg(chip->client, MAX17042_CONFIG, config->config);
+ max17042_write_reg(chip->client, MAX17042_LearnCFG, config->learn_cfg);
+ max17042_write_reg(chip->client, MAX17042_FilterCFG,
+ config->filter_cfg);
+ max17042_write_reg(chip->client, MAX17042_RelaxCFG, config->relax_cfg);
+}
+
+static void max17042_write_custom_regs(struct max17042_chip *chip)
+{
+ struct max17042_config_data *config = chip->pdata->config_data;
+
+ max17042_write_verify_reg(chip->client, MAX17042_RCOMP0,
+ config->rcomp0);
+ max17042_write_verify_reg(chip->client, MAX17042_TempCo,
+ config->tcompc0);
+ max17042_write_reg(chip->client, MAX17042_EmptyTempCo,
+ config->empty_tempco);
+ max17042_write_verify_reg(chip->client, MAX17042_K_empty0,
+ config->kempty0);
+ max17042_write_verify_reg(chip->client, MAX17042_ICHGTerm,
+ config->ichgt_term);
+}
+
+static void max17042_update_capacity_regs(struct max17042_chip *chip)
+{
+ struct max17042_config_data *config = chip->pdata->config_data;
+
+ max17042_write_verify_reg(chip->client, MAX17042_FullCAP,
+ config->fullcap);
+ max17042_write_reg(chip->client, MAX17042_DesignCap,
+ config->design_cap);
+ max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
+ config->fullcapnom);
+}
+
+static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip)
+{
+ u16 vfSoc;
+
+ vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC);
+ max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK);
+ max17042_write_verify_reg(chip->client, MAX17042_VFSOC0, vfSoc);
+ max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_LOCK);
+}
+
+static void max17042_load_new_capacity_params(struct max17042_chip *chip)
+{
+ u16 full_cap0, rep_cap, dq_acc, vfSoc;
+ u32 rem_cap;
+
+ struct max17042_config_data *config = chip->pdata->config_data;
+
+ full_cap0 = max17042_read_reg(chip->client, MAX17042_FullCAP0);
+ vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC);
+
+ /* fg_vfSoc needs to shifted by 8 bits to get the
+ * perc in 1% accuracy, to get the right rem_cap multiply
+ * full_cap0, fg_vfSoc and devide by 100
+ */
+ rem_cap = ((vfSoc >> 8) * full_cap0) / 100;
+ max17042_write_verify_reg(chip->client, MAX17042_RemCap, (u16)rem_cap);
+
+ rep_cap = (u16)rem_cap;
+ max17042_write_verify_reg(chip->client, MAX17042_RepCap, rep_cap);
+
+ /* Write dQ_acc to 200% of Capacity and dP_acc to 200% */
+ dq_acc = config->fullcap / dQ_ACC_DIV;
+ max17042_write_verify_reg(chip->client, MAX17042_dQacc, dq_acc);
+ max17042_write_verify_reg(chip->client, MAX17042_dPacc, dP_ACC_200);
+
+ max17042_write_verify_reg(chip->client, MAX17042_FullCAP,
+ config->fullcap);
+ max17042_write_reg(chip->client, MAX17042_DesignCap,
+ config->design_cap);
+ max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
+ config->fullcapnom);
+}
+
+/*
+ * Block write all the override values coming from platform data.
+ * This function MUST be called before the POR initialization proceedure
+ * specified by maxim.
+ */
+static inline void max17042_override_por_values(struct max17042_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ struct max17042_config_data *config = chip->pdata->config_data;
+
+ max17042_override_por(client, MAX17042_TGAIN, config->tgain);
+ max17042_override_por(client, MAx17042_TOFF, config->toff);
+ max17042_override_por(client, MAX17042_CGAIN, config->cgain);
+ max17042_override_por(client, MAX17042_COFF, config->coff);
+
+ max17042_override_por(client, MAX17042_VALRT_Th, config->valrt_thresh);
+ max17042_override_por(client, MAX17042_TALRT_Th, config->talrt_thresh);
+ max17042_override_por(client, MAX17042_SALRT_Th,
+ config->soc_alrt_thresh);
+ max17042_override_por(client, MAX17042_CONFIG, config->config);
+ max17042_override_por(client, MAX17042_SHDNTIMER, config->shdntimer);
+
+ max17042_override_por(client, MAX17042_DesignCap, config->design_cap);
+ max17042_override_por(client, MAX17042_ICHGTerm, config->ichgt_term);
+
+ max17042_override_por(client, MAX17042_AtRate, config->at_rate);
+ max17042_override_por(client, MAX17042_LearnCFG, config->learn_cfg);
+ max17042_override_por(client, MAX17042_FilterCFG, config->filter_cfg);
+ max17042_override_por(client, MAX17042_RelaxCFG, config->relax_cfg);
+ max17042_override_por(client, MAX17042_MiscCFG, config->misc_cfg);
+ max17042_override_por(client, MAX17042_MaskSOC, config->masksoc);
+
+ max17042_override_por(client, MAX17042_FullCAP, config->fullcap);
+ max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom);
+ max17042_override_por(client, MAX17042_SOC_empty, config->socempty);
+ max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty);
+ max17042_override_por(client, MAX17042_dQacc, config->dqacc);
+ max17042_override_por(client, MAX17042_dPacc, config->dpacc);
+
+ max17042_override_por(client, MAX17042_V_empty, config->vempty);
+ max17042_override_por(client, MAX17042_TempNom, config->temp_nom);
+ max17042_override_por(client, MAX17042_TempLim, config->temp_lim);
+ max17042_override_por(client, MAX17042_FCTC, config->fctc);
+ max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0);
+ max17042_override_por(client, MAX17042_TempCo, config->tcompc0);
+ max17042_override_por(client, MAX17042_EmptyTempCo,
+ config->empty_tempco);
+ max17042_override_por(client, MAX17042_K_empty0, config->kempty0);
+}
+
+static int max17042_init_chip(struct max17042_chip *chip)
+{
+ int ret;
+ int val;
+
+ max17042_override_por_values(chip);
+ /* After Power up, the MAX17042 requires 500mS in order
+ * to perform signal debouncing and initial SOC reporting
+ */
+ msleep(500);
+
+ /* Initialize configaration */
+ max17042_write_config_regs(chip);
+
+ /* write cell characterization data */
+ ret = max17042_init_model(chip);
+ if (ret) {
+ dev_err(&chip->client->dev, "%s init failed\n",
+ __func__);
+ return -EIO;
+ }
+ max17042_verify_model_lock(chip);
+ if (ret) {
+ dev_err(&chip->client->dev, "%s lock verify failed\n",
+ __func__);
+ return -EIO;
+ }
+ /* write custom parameters */
+ max17042_write_custom_regs(chip);
+
+ /* update capacity params */
+ max17042_update_capacity_regs(chip);
+
+ /* delay must be atleast 350mS to allow VFSOC
+ * to be calculated from the new configuration
+ */
+ msleep(350);
+
+ /* reset vfsoc0 reg */
+ max17042_reset_vfsoc0_reg(chip);
+
+ /* load new capacity params */
+ max17042_load_new_capacity_params(chip);
+
+ /* Init complete, Clear the POR bit */
+ val = max17042_read_reg(chip->client, MAX17042_STATUS);
+ max17042_write_reg(chip->client, MAX17042_STATUS,
+ val & (~STATUS_POR_BIT));
+ return 0;
+}
+
+static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
+{
+ u16 soc, soc_tr;
+
+ /* program interrupt thesholds such that we should
+ * get interrupt for every 'off' perc change in the soc
+ */
+ soc = max17042_read_reg(chip->client, MAX17042_RepSOC) >> 8;
+ soc_tr = (soc + off) << 8;
+ soc_tr |= (soc - off);
+ max17042_write_reg(chip->client, MAX17042_SALRT_Th, soc_tr);
+}
+
+static irqreturn_t max17042_thread_handler(int id, void *dev)
+{
+ struct max17042_chip *chip = dev;
+ u16 val;
+
+ val = max17042_read_reg(chip->client, MAX17042_STATUS);
+ if ((val & STATUS_INTR_SOCMIN_BIT) ||
+ (val & STATUS_INTR_SOCMAX_BIT)) {
+ dev_info(&chip->client->dev, "SOC threshold INTR\n");
+ max17042_set_soc_threshold(chip, 1);
+ }
+
+ power_supply_changed(&chip->battery);
+ return IRQ_HANDLED;
+}
+
+static void max17042_init_worker(struct work_struct *work)
+{
+ struct max17042_chip *chip = container_of(work,
+ struct max17042_chip, work);
+ int ret;
+
+ /* Initialize registers according to values from the platform data */
+ if (chip->pdata->enable_por_init && chip->pdata->config_data) {
+ ret = max17042_init_chip(chip);
+ if (ret)
+ return;
+ }
+
+ chip->init_complete = 1;
+}
+
+#ifdef CONFIG_OF
+static struct max17042_platform_data *
+max17042_get_pdata(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ u32 prop;
+ struct max17042_platform_data *pdata;
+
+ if (!np)
+ return dev->platform_data;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ /*
+ * Require current sense resistor value to be specified for
+ * current-sense functionality to be enabled at all.
+ */
+ if (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0) {
+ pdata->r_sns = prop;
+ pdata->enable_current_sense = true;
+ }
+
+ return pdata;
+}
+#else
+static struct max17042_platform_data *
+max17042_get_pdata(struct device *dev)
+{
+ return dev->platform_data;
+}
+#endif
+
static int __devinit max17042_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct max17042_chip *chip;
int ret;
+ int reg;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -EIO;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->client = client;
- chip->pdata = client->dev.platform_data;
+ chip->pdata = max17042_get_pdata(&client->dev);
+ if (!chip->pdata) {
+ dev_err(&client->dev, "no platform data provided\n");
+ return -EINVAL;
+ }
i2c_set_clientdata(client, chip);
@@ -243,17 +673,9 @@ static int __devinit max17042_probe(struct i2c_client *client,
if (chip->pdata->r_sns == 0)
chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
- ret = power_supply_register(&client->dev, &chip->battery);
- if (ret) {
- dev_err(&client->dev, "failed: power supply register\n");
- kfree(chip);
- return ret;
- }
-
- /* Initialize registers according to values from the platform data */
if (chip->pdata->init_data)
max17042_set_reg(client, chip->pdata->init_data,
- chip->pdata->num_init_data);
+ chip->pdata->num_init_data);
if (!chip->pdata->enable_current_sense) {
max17042_write_reg(client, MAX17042_CGAIN, 0x0000);
@@ -261,7 +683,34 @@ static int __devinit max17042_probe(struct i2c_client *client,
max17042_write_reg(client, MAX17042_LearnCFG, 0x0007);
}
- return 0;
+ if (client->irq) {
+ ret = request_threaded_irq(client->irq, NULL,
+ max17042_thread_handler,
+ IRQF_TRIGGER_FALLING,
+ chip->battery.name, chip);
+ if (!ret) {
+ reg = max17042_read_reg(client, MAX17042_CONFIG);
+ reg |= CONFIG_ALRT_BIT_ENBL;
+ max17042_write_reg(client, MAX17042_CONFIG, reg);
+ max17042_set_soc_threshold(chip, 1);
+ } else
+ dev_err(&client->dev, "%s(): cannot get IRQ\n",
+ __func__);
+ }
+
+ reg = max17042_read_reg(chip->client, MAX17042_STATUS);
+
+ if (reg & STATUS_POR_BIT) {
+ INIT_WORK(&chip->work, max17042_init_worker);
+ schedule_work(&chip->work);
+ } else {
+ chip->init_complete = 1;
+ }
+
+ ret = power_supply_register(&client->dev, &chip->battery);
+ if (ret)
+ dev_err(&client->dev, "failed: power supply register\n");
+ return ret;
}
static int __devexit max17042_remove(struct i2c_client *client)
@@ -269,10 +718,17 @@ static int __devexit max17042_remove(struct i2c_client *client)
struct max17042_chip *chip = i2c_get_clientdata(client);
power_supply_unregister(&chip->battery);
- kfree(chip);
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id max17042_dt_match[] = {
+ { .compatible = "maxim,max17042" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max17042_dt_match);
+#endif
+
static const struct i2c_device_id max17042_id[] = {
{ "max17042", 0 },
{ }
@@ -282,23 +738,13 @@ MODULE_DEVICE_TABLE(i2c, max17042_id);
static struct i2c_driver max17042_i2c_driver = {
.driver = {
.name = "max17042",
+ .of_match_table = of_match_ptr(max17042_dt_match),
},
.probe = max17042_probe,
.remove = __devexit_p(max17042_remove),
.id_table = max17042_id,
};
-
-static int __init max17042_init(void)
-{
- return i2c_add_driver(&max17042_i2c_driver);
-}
-module_init(max17042_init);
-
-static void __exit max17042_exit(void)
-{
- i2c_del_driver(&max17042_i2c_driver);
-}
-module_exit(max17042_exit);
+module_i2c_driver(max17042_i2c_driver);
MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
MODULE_DESCRIPTION("MAX17042 Fuel Gauge");
diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c
index 9ff8af0..06b659d 100644
--- a/drivers/power/sbs-battery.c
+++ b/drivers/power/sbs-battery.c
@@ -852,18 +852,7 @@ static struct i2c_driver sbs_battery_driver = {
.of_match_table = sbs_dt_ids,
},
};
-
-static int __init sbs_battery_init(void)
-{
- return i2c_add_driver(&sbs_battery_driver);
-}
-module_init(sbs_battery_init);
-
-static void __exit sbs_battery_exit(void)
-{
- i2c_del_driver(&sbs_battery_driver);
-}
-module_exit(sbs_battery_exit);
+module_i2c_driver(sbs_battery_driver);
MODULE_DESCRIPTION("SBS battery monitor driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c
new file mode 100644
index 0000000..ce1694d
--- /dev/null
+++ b/drivers/power/smb347-charger.c
@@ -0,0 +1,1294 @@
+/*
+ * Summit Microelectronics SMB347 Battery Charger Driver
+ *
+ * Copyright (C) 2011, Intel Corporation
+ *
+ * Authors: Bruce E. Robertson <bruce.e.robertson@intel.com>
+ * Mika Westerberg <mika.westerberg@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
+#include <linux/power/smb347-charger.h>
+#include <linux/seq_file.h>
+
+/*
+ * Configuration registers. These are mirrored to volatile RAM and can be
+ * written once %CMD_A_ALLOW_WRITE is set in %CMD_A register. They will be
+ * reloaded from non-volatile registers after POR.
+ */
+#define CFG_CHARGE_CURRENT 0x00
+#define CFG_CHARGE_CURRENT_FCC_MASK 0xe0
+#define CFG_CHARGE_CURRENT_FCC_SHIFT 5
+#define CFG_CHARGE_CURRENT_PCC_MASK 0x18
+#define CFG_CHARGE_CURRENT_PCC_SHIFT 3
+#define CFG_CHARGE_CURRENT_TC_MASK 0x07
+#define CFG_CURRENT_LIMIT 0x01
+#define CFG_CURRENT_LIMIT_DC_MASK 0xf0
+#define CFG_CURRENT_LIMIT_DC_SHIFT 4
+#define CFG_CURRENT_LIMIT_USB_MASK 0x0f
+#define CFG_FLOAT_VOLTAGE 0x03
+#define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK 0xc0
+#define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT 6
+#define CFG_STAT 0x05
+#define CFG_STAT_DISABLED BIT(5)
+#define CFG_STAT_ACTIVE_HIGH BIT(7)
+#define CFG_PIN 0x06
+#define CFG_PIN_EN_CTRL_MASK 0x60
+#define CFG_PIN_EN_CTRL_ACTIVE_HIGH 0x40
+#define CFG_PIN_EN_CTRL_ACTIVE_LOW 0x60
+#define CFG_PIN_EN_APSD_IRQ BIT(1)
+#define CFG_PIN_EN_CHARGER_ERROR BIT(2)
+#define CFG_THERM 0x07
+#define CFG_THERM_SOFT_HOT_COMPENSATION_MASK 0x03
+#define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT 0
+#define CFG_THERM_SOFT_COLD_COMPENSATION_MASK 0x0c
+#define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2
+#define CFG_THERM_MONITOR_DISABLED BIT(4)
+#define CFG_SYSOK 0x08
+#define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED BIT(2)
+#define CFG_OTHER 0x09
+#define CFG_OTHER_RID_MASK 0xc0
+#define CFG_OTHER_RID_ENABLED_AUTO_OTG 0xc0
+#define CFG_OTG 0x0a
+#define CFG_OTG_TEMP_THRESHOLD_MASK 0x30
+#define CFG_OTG_TEMP_THRESHOLD_SHIFT 4
+#define CFG_OTG_CC_COMPENSATION_MASK 0xc0
+#define CFG_OTG_CC_COMPENSATION_SHIFT 6
+#define CFG_TEMP_LIMIT 0x0b
+#define CFG_TEMP_LIMIT_SOFT_HOT_MASK 0x03
+#define CFG_TEMP_LIMIT_SOFT_HOT_SHIFT 0
+#define CFG_TEMP_LIMIT_SOFT_COLD_MASK 0x0c
+#define CFG_TEMP_LIMIT_SOFT_COLD_SHIFT 2
+#define CFG_TEMP_LIMIT_HARD_HOT_MASK 0x30
+#define CFG_TEMP_LIMIT_HARD_HOT_SHIFT 4
+#define CFG_TEMP_LIMIT_HARD_COLD_MASK 0xc0
+#define CFG_TEMP_LIMIT_HARD_COLD_SHIFT 6
+#define CFG_FAULT_IRQ 0x0c
+#define CFG_FAULT_IRQ_DCIN_UV BIT(2)
+#define CFG_STATUS_IRQ 0x0d
+#define CFG_STATUS_IRQ_TERMINATION_OR_TAPER BIT(4)
+#define CFG_ADDRESS 0x0e
+
+/* Command registers */
+#define CMD_A 0x30
+#define CMD_A_CHG_ENABLED BIT(1)
+#define CMD_A_SUSPEND_ENABLED BIT(2)
+#define CMD_A_ALLOW_WRITE BIT(7)
+#define CMD_B 0x31
+#define CMD_C 0x33
+
+/* Interrupt Status registers */
+#define IRQSTAT_A 0x35
+#define IRQSTAT_C 0x37
+#define IRQSTAT_C_TERMINATION_STAT BIT(0)
+#define IRQSTAT_C_TERMINATION_IRQ BIT(1)
+#define IRQSTAT_C_TAPER_IRQ BIT(3)
+#define IRQSTAT_E 0x39
+#define IRQSTAT_E_USBIN_UV_STAT BIT(0)
+#define IRQSTAT_E_USBIN_UV_IRQ BIT(1)
+#define IRQSTAT_E_DCIN_UV_STAT BIT(4)
+#define IRQSTAT_E_DCIN_UV_IRQ BIT(5)
+#define IRQSTAT_F 0x3a
+
+/* Status registers */
+#define STAT_A 0x3b
+#define STAT_A_FLOAT_VOLTAGE_MASK 0x3f
+#define STAT_B 0x3c
+#define STAT_C 0x3d
+#define STAT_C_CHG_ENABLED BIT(0)
+#define STAT_C_CHG_MASK 0x06
+#define STAT_C_CHG_SHIFT 1
+#define STAT_C_CHARGER_ERROR BIT(6)
+#define STAT_E 0x3f
+
+/**
+ * struct smb347_charger - smb347 charger instance
+ * @lock: protects concurrent access to online variables
+ * @client: pointer to i2c client
+ * @mains: power_supply instance for AC/DC power
+ * @usb: power_supply instance for USB power
+ * @battery: power_supply instance for battery
+ * @mains_online: is AC/DC input connected
+ * @usb_online: is USB input connected
+ * @charging_enabled: is charging enabled
+ * @dentry: for debugfs
+ * @pdata: pointer to platform data
+ */
+struct smb347_charger {
+ struct mutex lock;
+ struct i2c_client *client;
+ struct power_supply mains;
+ struct power_supply usb;
+ struct power_supply battery;
+ bool mains_online;
+ bool usb_online;
+ bool charging_enabled;
+ struct dentry *dentry;
+ const struct smb347_charger_platform_data *pdata;
+};
+
+/* Fast charge current in uA */
+static const unsigned int fcc_tbl[] = {
+ 700000,
+ 900000,
+ 1200000,
+ 1500000,
+ 1800000,
+ 2000000,
+ 2200000,
+ 2500000,
+};
+
+/* Pre-charge current in uA */
+static const unsigned int pcc_tbl[] = {
+ 100000,
+ 150000,
+ 200000,
+ 250000,
+};
+
+/* Termination current in uA */
+static const unsigned int tc_tbl[] = {
+ 37500,
+ 50000,
+ 100000,
+ 150000,
+ 200000,
+ 250000,
+ 500000,
+ 600000,
+};
+
+/* Input current limit in uA */
+static const unsigned int icl_tbl[] = {
+ 300000,
+ 500000,
+ 700000,
+ 900000,
+ 1200000,
+ 1500000,
+ 1800000,
+ 2000000,
+ 2200000,
+ 2500000,
+};
+
+/* Charge current compensation in uA */
+static const unsigned int ccc_tbl[] = {
+ 250000,
+ 700000,
+ 900000,
+ 1200000,
+};
+
+/* Convert register value to current using lookup table */
+static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val)
+{
+ if (val >= size)
+ return -EINVAL;
+ return tbl[val];
+}
+
+/* Convert current to register value using lookup table */
+static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (val < tbl[i])
+ break;
+ return i > 0 ? i - 1 : -EINVAL;
+}
+
+static int smb347_read(struct smb347_charger *smb, u8 reg)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(smb->client, reg);
+ if (ret < 0)
+ dev_warn(&smb->client->dev, "failed to read reg 0x%x: %d\n",
+ reg, ret);
+ return ret;
+}
+
+static int smb347_write(struct smb347_charger *smb, u8 reg, u8 val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(smb->client, reg, val);
+ if (ret < 0)
+ dev_warn(&smb->client->dev, "failed to write reg 0x%x: %d\n",
+ reg, ret);
+ return ret;
+}
+
+/**
+ * smb347_update_status - updates the charging status
+ * @smb: pointer to smb347 charger instance
+ *
+ * Function checks status of the charging and updates internal state
+ * accordingly. Returns %0 if there is no change in status, %1 if the
+ * status has changed and negative errno in case of failure.
+ */
+static int smb347_update_status(struct smb347_charger *smb)
+{
+ bool usb = false;
+ bool dc = false;
+ int ret;
+
+ ret = smb347_read(smb, IRQSTAT_E);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Dc and usb are set depending on whether they are enabled in
+ * platform data _and_ whether corresponding undervoltage is set.
+ */
+ if (smb->pdata->use_mains)
+ dc = !(ret & IRQSTAT_E_DCIN_UV_STAT);
+ if (smb->pdata->use_usb)
+ usb = !(ret & IRQSTAT_E_USBIN_UV_STAT);
+
+ mutex_lock(&smb->lock);
+ ret = smb->mains_online != dc || smb->usb_online != usb;
+ smb->mains_online = dc;
+ smb->usb_online = usb;
+ mutex_unlock(&smb->lock);
+
+ return ret;
+}
+
+/*
+ * smb347_is_online - returns whether input power source is connected
+ * @smb: pointer to smb347 charger instance
+ *
+ * Returns %true if input power source is connected. Note that this is
+ * dependent on what platform has configured for usable power sources. For
+ * example if USB is disabled, this will return %false even if the USB
+ * cable is connected.
+ */
+static bool smb347_is_online(struct smb347_charger *smb)
+{
+ bool ret;
+
+ mutex_lock(&smb->lock);
+ ret = smb->usb_online || smb->mains_online;
+ mutex_unlock(&smb->lock);
+
+ return ret;
+}
+
+/**
+ * smb347_charging_status - returns status of charging
+ * @smb: pointer to smb347 charger instance
+ *
+ * Function returns charging status. %0 means no charging is in progress,
+ * %1 means pre-charging, %2 fast-charging and %3 taper-charging.
+ */
+static int smb347_charging_status(struct smb347_charger *smb)
+{
+ int ret;
+
+ if (!smb347_is_online(smb))
+ return 0;
+
+ ret = smb347_read(smb, STAT_C);
+ if (ret < 0)
+ return 0;
+
+ return (ret & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT;
+}
+
+static int smb347_charging_set(struct smb347_charger *smb, bool enable)
+{
+ int ret = 0;
+
+ if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) {
+ dev_dbg(&smb->client->dev,
+ "charging enable/disable in SW disabled\n");
+ return 0;
+ }
+
+ mutex_lock(&smb->lock);
+ if (smb->charging_enabled != enable) {
+ ret = smb347_read(smb, CMD_A);
+ if (ret < 0)
+ goto out;
+
+ smb->charging_enabled = enable;
+
+ if (enable)
+ ret |= CMD_A_CHG_ENABLED;
+ else
+ ret &= ~CMD_A_CHG_ENABLED;
+
+ ret = smb347_write(smb, CMD_A, ret);
+ }
+out:
+ mutex_unlock(&smb->lock);
+ return ret;
+}
+
+static inline int smb347_charging_enable(struct smb347_charger *smb)
+{
+ return smb347_charging_set(smb, true);
+}
+
+static inline int smb347_charging_disable(struct smb347_charger *smb)
+{
+ return smb347_charging_set(smb, false);
+}
+
+static int smb347_update_online(struct smb347_charger *smb)
+{
+ int ret;
+
+ /*
+ * Depending on whether valid power source is connected or not, we
+ * disable or enable the charging. We do it manually because it
+ * depends on how the platform has configured the valid inputs.
+ */
+ if (smb347_is_online(smb)) {
+ ret = smb347_charging_enable(smb);
+ if (ret < 0)
+ dev_err(&smb->client->dev,
+ "failed to enable charging\n");
+ } else {
+ ret = smb347_charging_disable(smb);
+ if (ret < 0)
+ dev_err(&smb->client->dev,
+ "failed to disable charging\n");
+ }
+
+ return ret;
+}
+
+static int smb347_set_charge_current(struct smb347_charger *smb)
+{
+ int ret, val;
+
+ ret = smb347_read(smb, CFG_CHARGE_CURRENT);
+ if (ret < 0)
+ return ret;
+
+ if (smb->pdata->max_charge_current) {
+ val = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl),
+ smb->pdata->max_charge_current);
+ if (val < 0)
+ return val;
+
+ ret &= ~CFG_CHARGE_CURRENT_FCC_MASK;
+ ret |= val << CFG_CHARGE_CURRENT_FCC_SHIFT;
+ }
+
+ if (smb->pdata->pre_charge_current) {
+ val = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl),
+ smb->pdata->pre_charge_current);
+ if (val < 0)
+ return val;
+
+ ret &= ~CFG_CHARGE_CURRENT_PCC_MASK;
+ ret |= val << CFG_CHARGE_CURRENT_PCC_SHIFT;
+ }
+
+ if (smb->pdata->termination_current) {
+ val = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl),
+ smb->pdata->termination_current);
+ if (val < 0)
+ return val;
+
+ ret &= ~CFG_CHARGE_CURRENT_TC_MASK;
+ ret |= val;
+ }
+
+ return smb347_write(smb, CFG_CHARGE_CURRENT, ret);
+}
+
+static int smb347_set_current_limits(struct smb347_charger *smb)
+{
+ int ret, val;
+
+ ret = smb347_read(smb, CFG_CURRENT_LIMIT);
+ if (ret < 0)
+ return ret;
+
+ if (smb->pdata->mains_current_limit) {
+ val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
+ smb->pdata->mains_current_limit);
+ if (val < 0)
+ return val;
+
+ ret &= ~CFG_CURRENT_LIMIT_DC_MASK;
+ ret |= val << CFG_CURRENT_LIMIT_DC_SHIFT;
+ }
+
+ if (smb->pdata->usb_hc_current_limit) {
+ val = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
+ smb->pdata->usb_hc_current_limit);
+ if (val < 0)
+ return val;
+
+ ret &= ~CFG_CURRENT_LIMIT_USB_MASK;
+ ret |= val;
+ }
+
+ return smb347_write(smb, CFG_CURRENT_LIMIT, ret);
+}
+
+static int smb347_set_voltage_limits(struct smb347_charger *smb)
+{
+ int ret, val;
+
+ ret = smb347_read(smb, CFG_FLOAT_VOLTAGE);
+ if (ret < 0)
+ return ret;
+
+ if (smb->pdata->pre_to_fast_voltage) {
+ val = smb->pdata->pre_to_fast_voltage;
+
+ /* uV */
+ val = clamp_val(val, 2400000, 3000000) - 2400000;
+ val /= 200000;
+
+ ret &= ~CFG_FLOAT_VOLTAGE_THRESHOLD_MASK;
+ ret |= val << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT;
+ }
+
+ if (smb->pdata->max_charge_voltage) {
+ val = smb->pdata->max_charge_voltage;
+
+ /* uV */
+ val = clamp_val(val, 3500000, 4500000) - 3500000;
+ val /= 20000;
+
+ ret |= val;
+ }
+
+ return smb347_write(smb, CFG_FLOAT_VOLTAGE, ret);
+}
+
+static int smb347_set_temp_limits(struct smb347_charger *smb)
+{
+ bool enable_therm_monitor = false;
+ int ret, val;
+
+ if (smb->pdata->chip_temp_threshold) {
+ val = smb->pdata->chip_temp_threshold;
+
+ /* degree C */
+ val = clamp_val(val, 100, 130) - 100;
+ val /= 10;
+
+ ret = smb347_read(smb, CFG_OTG);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~CFG_OTG_TEMP_THRESHOLD_MASK;
+ ret |= val << CFG_OTG_TEMP_THRESHOLD_SHIFT;
+
+ ret = smb347_write(smb, CFG_OTG, ret);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = smb347_read(smb, CFG_TEMP_LIMIT);
+ if (ret < 0)
+ return ret;
+
+ if (smb->pdata->soft_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) {
+ val = smb->pdata->soft_cold_temp_limit;
+
+ val = clamp_val(val, 0, 15);
+ val /= 5;
+ /* this goes from higher to lower so invert the value */
+ val = ~val & 0x3;
+
+ ret &= ~CFG_TEMP_LIMIT_SOFT_COLD_MASK;
+ ret |= val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT;
+
+ enable_therm_monitor = true;
+ }
+
+ if (smb->pdata->soft_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) {
+ val = smb->pdata->soft_hot_temp_limit;
+
+ val = clamp_val(val, 40, 55) - 40;
+ val /= 5;
+
+ ret &= ~CFG_TEMP_LIMIT_SOFT_HOT_MASK;
+ ret |= val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT;
+
+ enable_therm_monitor = true;
+ }
+
+ if (smb->pdata->hard_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) {
+ val = smb->pdata->hard_cold_temp_limit;
+
+ val = clamp_val(val, -5, 10) + 5;
+ val /= 5;
+ /* this goes from higher to lower so invert the value */
+ val = ~val & 0x3;
+
+ ret &= ~CFG_TEMP_LIMIT_HARD_COLD_MASK;
+ ret |= val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT;
+
+ enable_therm_monitor = true;
+ }
+
+ if (smb->pdata->hard_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) {
+ val = smb->pdata->hard_hot_temp_limit;
+
+ val = clamp_val(val, 50, 65) - 50;
+ val /= 5;
+
+ ret &= ~CFG_TEMP_LIMIT_HARD_HOT_MASK;
+ ret |= val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT;
+
+ enable_therm_monitor = true;
+ }
+
+ ret = smb347_write(smb, CFG_TEMP_LIMIT, ret);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * If any of the temperature limits are set, we also enable the
+ * thermistor monitoring.
+ *
+ * When soft limits are hit, the device will start to compensate
+ * current and/or voltage depending on the configuration.
+ *
+ * When hard limit is hit, the device will suspend charging
+ * depending on the configuration.
+ */
+ if (enable_therm_monitor) {
+ ret = smb347_read(smb, CFG_THERM);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~CFG_THERM_MONITOR_DISABLED;
+
+ ret = smb347_write(smb, CFG_THERM, ret);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (smb->pdata->suspend_on_hard_temp_limit) {
+ ret = smb347_read(smb, CFG_SYSOK);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED;
+
+ ret = smb347_write(smb, CFG_SYSOK, ret);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (smb->pdata->soft_temp_limit_compensation !=
+ SMB347_SOFT_TEMP_COMPENSATE_DEFAULT) {
+ val = smb->pdata->soft_temp_limit_compensation & 0x3;
+
+ ret = smb347_read(smb, CFG_THERM);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~CFG_THERM_SOFT_HOT_COMPENSATION_MASK;
+ ret |= val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT;
+
+ ret &= ~CFG_THERM_SOFT_COLD_COMPENSATION_MASK;
+ ret |= val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT;
+
+ ret = smb347_write(smb, CFG_THERM, ret);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (smb->pdata->charge_current_compensation) {
+ val = current_to_hw(ccc_tbl, ARRAY_SIZE(ccc_tbl),
+ smb->pdata->charge_current_compensation);
+ if (val < 0)
+ return val;
+
+ ret = smb347_read(smb, CFG_OTG);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~CFG_OTG_CC_COMPENSATION_MASK;
+ ret |= (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT;
+
+ ret = smb347_write(smb, CFG_OTG, ret);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * smb347_set_writable - enables/disables writing to non-volatile registers
+ * @smb: pointer to smb347 charger instance
+ *
+ * You can enable/disable writing to the non-volatile configuration
+ * registers by calling this function.
+ *
+ * Returns %0 on success and negative errno in case of failure.
+ */
+static int smb347_set_writable(struct smb347_charger *smb, bool writable)
+{
+ int ret;
+
+ ret = smb347_read(smb, CMD_A);
+ if (ret < 0)
+ return ret;
+
+ if (writable)
+ ret |= CMD_A_ALLOW_WRITE;
+ else
+ ret &= ~CMD_A_ALLOW_WRITE;
+
+ return smb347_write(smb, CMD_A, ret);
+}
+
+static int smb347_hw_init(struct smb347_charger *smb)
+{
+ int ret;
+
+ ret = smb347_set_writable(smb, true);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Program the platform specific configuration values to the device
+ * first.
+ */
+ ret = smb347_set_charge_current(smb);
+ if (ret < 0)
+ goto fail;
+
+ ret = smb347_set_current_limits(smb);
+ if (ret < 0)
+ goto fail;
+
+ ret = smb347_set_voltage_limits(smb);
+ if (ret < 0)
+ goto fail;
+
+ ret = smb347_set_temp_limits(smb);
+ if (ret < 0)
+ goto fail;
+
+ /* If USB charging is disabled we put the USB in suspend mode */
+ if (!smb->pdata->use_usb) {
+ ret = smb347_read(smb, CMD_A);
+ if (ret < 0)
+ goto fail;
+
+ ret |= CMD_A_SUSPEND_ENABLED;
+
+ ret = smb347_write(smb, CMD_A, ret);
+ if (ret < 0)
+ goto fail;
+ }
+
+ ret = smb347_read(smb, CFG_OTHER);
+ if (ret < 0)
+ goto fail;
+
+ /*
+ * If configured by platform data, we enable hardware Auto-OTG
+ * support for driving VBUS. Otherwise we disable it.
+ */
+ ret &= ~CFG_OTHER_RID_MASK;
+ if (smb->pdata->use_usb_otg)
+ ret |= CFG_OTHER_RID_ENABLED_AUTO_OTG;
+
+ ret = smb347_write(smb, CFG_OTHER, ret);
+ if (ret < 0)
+ goto fail;
+
+ ret = smb347_read(smb, CFG_PIN);
+ if (ret < 0)
+ goto fail;
+
+ /*
+ * Make the charging functionality controllable by a write to the
+ * command register unless pin control is specified in the platform
+ * data.
+ */
+ ret &= ~CFG_PIN_EN_CTRL_MASK;
+
+ switch (smb->pdata->enable_control) {
+ case SMB347_CHG_ENABLE_SW:
+ /* Do nothing, 0 means i2c control */
+ break;
+ case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW:
+ ret |= CFG_PIN_EN_CTRL_ACTIVE_LOW;
+ break;
+ case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH:
+ ret |= CFG_PIN_EN_CTRL_ACTIVE_HIGH;
+ break;
+ }
+
+ /* Disable Automatic Power Source Detection (APSD) interrupt. */
+ ret &= ~CFG_PIN_EN_APSD_IRQ;
+
+ ret = smb347_write(smb, CFG_PIN, ret);
+ if (ret < 0)
+ goto fail;
+
+ ret = smb347_update_status(smb);
+ if (ret < 0)
+ goto fail;
+
+ ret = smb347_update_online(smb);
+
+fail:
+ smb347_set_writable(smb, false);
+ return ret;
+}
+
+static irqreturn_t smb347_interrupt(int irq, void *data)
+{
+ struct smb347_charger *smb = data;
+ int stat_c, irqstat_e, irqstat_c;
+ irqreturn_t ret = IRQ_NONE;
+
+ stat_c = smb347_read(smb, STAT_C);
+ if (stat_c < 0) {
+ dev_warn(&smb->client->dev, "reading STAT_C failed\n");
+ return IRQ_NONE;
+ }
+
+ irqstat_c = smb347_read(smb, IRQSTAT_C);
+ if (irqstat_c < 0) {
+ dev_warn(&smb->client->dev, "reading IRQSTAT_C failed\n");
+ return IRQ_NONE;
+ }
+
+ irqstat_e = smb347_read(smb, IRQSTAT_E);
+ if (irqstat_e < 0) {
+ dev_warn(&smb->client->dev, "reading IRQSTAT_E failed\n");
+ return IRQ_NONE;
+ }
+
+ /*
+ * If we get charger error we report the error back to user and
+ * disable charging.
+ */
+ if (stat_c & STAT_C_CHARGER_ERROR) {
+ dev_err(&smb->client->dev,
+ "error in charger, disabling charging\n");
+
+ smb347_charging_disable(smb);
+ power_supply_changed(&smb->battery);
+
+ ret = IRQ_HANDLED;
+ }
+
+ /*
+ * If we reached the termination current the battery is charged and
+ * we can update the status now. Charging is automatically
+ * disabled by the hardware.
+ */
+ if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) {
+ if (irqstat_c & IRQSTAT_C_TERMINATION_STAT)
+ power_supply_changed(&smb->battery);
+ ret = IRQ_HANDLED;
+ }
+
+ /*
+ * If we got an under voltage interrupt it means that AC/USB input
+ * was connected or disconnected.
+ */
+ if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) {
+ if (smb347_update_status(smb) > 0) {
+ smb347_update_online(smb);
+ power_supply_changed(&smb->mains);
+ power_supply_changed(&smb->usb);
+ }
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int smb347_irq_set(struct smb347_charger *smb, bool enable)
+{
+ int ret;
+
+ ret = smb347_set_writable(smb, true);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Enable/disable interrupts for:
+ * - under voltage
+ * - termination current reached
+ * - charger error
+ */
+ if (enable) {
+ ret = smb347_write(smb, CFG_FAULT_IRQ, CFG_FAULT_IRQ_DCIN_UV);
+ if (ret < 0)
+ goto fail;
+
+ ret = smb347_write(smb, CFG_STATUS_IRQ,
+ CFG_STATUS_IRQ_TERMINATION_OR_TAPER);
+ if (ret < 0)
+ goto fail;
+
+ ret = smb347_read(smb, CFG_PIN);
+ if (ret < 0)
+ goto fail;
+
+ ret |= CFG_PIN_EN_CHARGER_ERROR;
+
+ ret = smb347_write(smb, CFG_PIN, ret);
+ } else {
+ ret = smb347_write(smb, CFG_FAULT_IRQ, 0);
+ if (ret < 0)
+ goto fail;
+
+ ret = smb347_write(smb, CFG_STATUS_IRQ, 0);
+ if (ret < 0)
+ goto fail;
+
+ ret = smb347_read(smb, CFG_PIN);
+ if (ret < 0)
+ goto fail;
+
+ ret &= ~CFG_PIN_EN_CHARGER_ERROR;
+
+ ret = smb347_write(smb, CFG_PIN, ret);
+ }
+
+fail:
+ smb347_set_writable(smb, false);
+ return ret;
+}
+
+static inline int smb347_irq_enable(struct smb347_charger *smb)
+{
+ return smb347_irq_set(smb, true);
+}
+
+static inline int smb347_irq_disable(struct smb347_charger *smb)
+{
+ return smb347_irq_set(smb, false);
+}
+
+static int smb347_irq_init(struct smb347_charger *smb)
+{
+ const struct smb347_charger_platform_data *pdata = smb->pdata;
+ int ret, irq = gpio_to_irq(pdata->irq_gpio);
+
+ ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, smb->client->name);
+ if (ret < 0)
+ goto fail;
+
+ ret = request_threaded_irq(irq, NULL, smb347_interrupt,
+ IRQF_TRIGGER_FALLING, smb->client->name,
+ smb);
+ if (ret < 0)
+ goto fail_gpio;
+
+ ret = smb347_set_writable(smb, true);
+ if (ret < 0)
+ goto fail_irq;
+
+ /*
+ * Configure the STAT output to be suitable for interrupts: disable
+ * all other output (except interrupts) and make it active low.
+ */
+ ret = smb347_read(smb, CFG_STAT);
+ if (ret < 0)
+ goto fail_readonly;
+
+ ret &= ~CFG_STAT_ACTIVE_HIGH;
+ ret |= CFG_STAT_DISABLED;
+
+ ret = smb347_write(smb, CFG_STAT, ret);
+ if (ret < 0)
+ goto fail_readonly;
+
+ ret = smb347_irq_enable(smb);
+ if (ret < 0)
+ goto fail_readonly;
+
+ smb347_set_writable(smb, false);
+ smb->client->irq = irq;
+ return 0;
+
+fail_readonly:
+ smb347_set_writable(smb, false);
+fail_irq:
+ free_irq(irq, smb);
+fail_gpio:
+ gpio_free(pdata->irq_gpio);
+fail:
+ smb->client->irq = 0;
+ return ret;
+}
+
+static int smb347_mains_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct smb347_charger *smb =
+ container_of(psy, struct smb347_charger, mains);
+
+ if (prop == POWER_SUPPLY_PROP_ONLINE) {
+ val->intval = smb->mains_online;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static enum power_supply_property smb347_mains_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int smb347_usb_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct smb347_charger *smb =
+ container_of(psy, struct smb347_charger, usb);
+
+ if (prop == POWER_SUPPLY_PROP_ONLINE) {
+ val->intval = smb->usb_online;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static enum power_supply_property smb347_usb_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int smb347_battery_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct smb347_charger *smb =
+ container_of(psy, struct smb347_charger, battery);
+ const struct smb347_charger_platform_data *pdata = smb->pdata;
+ int ret;
+
+ ret = smb347_update_status(smb);
+ if (ret < 0)
+ return ret;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (!smb347_is_online(smb)) {
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ }
+ if (smb347_charging_status(smb))
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ if (!smb347_is_online(smb))
+ return -ENODATA;
+
+ /*
+ * We handle trickle and pre-charging the same, and taper
+ * and none the same.
+ */
+ switch (smb347_charging_status(smb)) {
+ case 1:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ break;
+ case 2:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ break;
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = pdata->battery_info.technology;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = pdata->battery_info.voltage_min_design;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = pdata->battery_info.voltage_max_design;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (!smb347_is_online(smb))
+ return -ENODATA;
+ ret = smb347_read(smb, STAT_A);
+ if (ret < 0)
+ return ret;
+
+ ret &= STAT_A_FLOAT_VOLTAGE_MASK;
+ if (ret > 0x3d)
+ ret = 0x3d;
+
+ val->intval = 3500000 + ret * 20000;
+ break;
+
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ if (!smb347_is_online(smb))
+ return -ENODATA;
+
+ ret = smb347_read(smb, STAT_B);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The current value is composition of FCC and PCC values
+ * and we can detect which table to use from bit 5.
+ */
+ if (ret & 0x20) {
+ val->intval = hw_to_current(fcc_tbl,
+ ARRAY_SIZE(fcc_tbl),
+ ret & 7);
+ } else {
+ ret >>= 3;
+ val->intval = hw_to_current(pcc_tbl,
+ ARRAY_SIZE(pcc_tbl),
+ ret & 7);
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = pdata->battery_info.charge_full_design;
+ break;
+
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = pdata->battery_info.name;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static enum power_supply_property smb347_battery_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+};
+
+static int smb347_debugfs_show(struct seq_file *s, void *data)
+{
+ struct smb347_charger *smb = s->private;
+ int ret;
+ u8 reg;
+
+ seq_printf(s, "Control registers:\n");
+ seq_printf(s, "==================\n");
+ for (reg = CFG_CHARGE_CURRENT; reg <= CFG_ADDRESS; reg++) {
+ ret = smb347_read(smb, reg);
+ seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret);
+ }
+ seq_printf(s, "\n");
+
+ seq_printf(s, "Command registers:\n");
+ seq_printf(s, "==================\n");
+ ret = smb347_read(smb, CMD_A);
+ seq_printf(s, "0x%02x:\t0x%02x\n", CMD_A, ret);
+ ret = smb347_read(smb, CMD_B);
+ seq_printf(s, "0x%02x:\t0x%02x\n", CMD_B, ret);
+ ret = smb347_read(smb, CMD_C);
+ seq_printf(s, "0x%02x:\t0x%02x\n", CMD_C, ret);
+ seq_printf(s, "\n");
+
+ seq_printf(s, "Interrupt status registers:\n");
+ seq_printf(s, "===========================\n");
+ for (reg = IRQSTAT_A; reg <= IRQSTAT_F; reg++) {
+ ret = smb347_read(smb, reg);
+ seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret);
+ }
+ seq_printf(s, "\n");
+
+ seq_printf(s, "Status registers:\n");
+ seq_printf(s, "=================\n");
+ for (reg = STAT_A; reg <= STAT_E; reg++) {
+ ret = smb347_read(smb, reg);
+ seq_printf(s, "0x%02x:\t0x%02x\n", reg, ret);
+ }
+
+ return 0;
+}
+
+static int smb347_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, smb347_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations smb347_debugfs_fops = {
+ .open = smb347_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int smb347_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ static char *battery[] = { "smb347-battery" };
+ const struct smb347_charger_platform_data *pdata;
+ struct device *dev = &client->dev;
+ struct smb347_charger *smb;
+ int ret;
+
+ pdata = dev->platform_data;
+ if (!pdata)
+ return -EINVAL;
+
+ if (!pdata->use_mains && !pdata->use_usb)
+ return -EINVAL;
+
+ smb = devm_kzalloc(dev, sizeof(*smb), GFP_KERNEL);
+ if (!smb)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, smb);
+
+ mutex_init(&smb->lock);
+ smb->client = client;
+ smb->pdata = pdata;
+
+ ret = smb347_hw_init(smb);
+ if (ret < 0)
+ return ret;
+
+ smb->mains.name = "smb347-mains";
+ smb->mains.type = POWER_SUPPLY_TYPE_MAINS;
+ smb->mains.get_property = smb347_mains_get_property;
+ smb->mains.properties = smb347_mains_properties;
+ smb->mains.num_properties = ARRAY_SIZE(smb347_mains_properties);
+ smb->mains.supplied_to = battery;
+ smb->mains.num_supplicants = ARRAY_SIZE(battery);
+
+ smb->usb.name = "smb347-usb";
+ smb->usb.type = POWER_SUPPLY_TYPE_USB;
+ smb->usb.get_property = smb347_usb_get_property;
+ smb->usb.properties = smb347_usb_properties;
+ smb->usb.num_properties = ARRAY_SIZE(smb347_usb_properties);
+ smb->usb.supplied_to = battery;
+ smb->usb.num_supplicants = ARRAY_SIZE(battery);
+
+ smb->battery.name = "smb347-battery";
+ smb->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+ smb->battery.get_property = smb347_battery_get_property;
+ smb->battery.properties = smb347_battery_properties;
+ smb->battery.num_properties = ARRAY_SIZE(smb347_battery_properties);
+
+ ret = power_supply_register(dev, &smb->mains);
+ if (ret < 0)
+ return ret;
+
+ ret = power_supply_register(dev, &smb->usb);
+ if (ret < 0) {
+ power_supply_unregister(&smb->mains);
+ return ret;
+ }
+
+ ret = power_supply_register(dev, &smb->battery);
+ if (ret < 0) {
+ power_supply_unregister(&smb->usb);
+ power_supply_unregister(&smb->mains);
+ return ret;
+ }
+
+ /*
+ * Interrupt pin is optional. If it is connected, we setup the
+ * interrupt support here.
+ */
+ if (pdata->irq_gpio >= 0) {
+ ret = smb347_irq_init(smb);
+ if (ret < 0) {
+ dev_warn(dev, "failed to initialize IRQ: %d\n", ret);
+ dev_warn(dev, "disabling IRQ support\n");
+ }
+ }
+
+ smb->dentry = debugfs_create_file("smb347-regs", S_IRUSR, NULL, smb,
+ &smb347_debugfs_fops);
+ return 0;
+}
+
+static int smb347_remove(struct i2c_client *client)
+{
+ struct smb347_charger *smb = i2c_get_clientdata(client);
+
+ if (!IS_ERR_OR_NULL(smb->dentry))
+ debugfs_remove(smb->dentry);
+
+ if (client->irq) {
+ smb347_irq_disable(smb);
+ free_irq(client->irq, smb);
+ gpio_free(smb->pdata->irq_gpio);
+ }
+
+ power_supply_unregister(&smb->battery);
+ power_supply_unregister(&smb->usb);
+ power_supply_unregister(&smb->mains);
+ return 0;
+}
+
+static const struct i2c_device_id smb347_id[] = {
+ { "smb347", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, smb347_id);
+
+static struct i2c_driver smb347_driver = {
+ .driver = {
+ .name = "smb347",
+ },
+ .probe = smb347_probe,
+ .remove = __devexit_p(smb347_remove),
+ .id_table = smb347_id,
+};
+
+static int __init smb347_init(void)
+{
+ return i2c_add_driver(&smb347_driver);
+}
+module_init(smb347_init);
+
+static void __exit smb347_exit(void)
+{
+ i2c_del_driver(&smb347_driver);
+}
+module_exit(smb347_exit);
+
+MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_DESCRIPTION("SMB347 battery charger driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("i2c:smb347");
diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c
index 636ebb2..8c9a607 100644
--- a/drivers/power/z2_battery.c
+++ b/drivers/power/z2_battery.c
@@ -316,19 +316,7 @@ static struct i2c_driver z2_batt_driver = {
.remove = __devexit_p(z2_batt_remove),
.id_table = z2_batt_id,
};
-
-static int __init z2_batt_init(void)
-{
- return i2c_add_driver(&z2_batt_driver);
-}
-
-static void __exit z2_batt_exit(void)
-{
- i2c_del_driver(&z2_batt_driver);
-}
-
-module_init(z2_batt_init);
-module_exit(z2_batt_exit);
+module_i2c_driver(z2_batt_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>");
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index dc87eda..eb415bd 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -458,6 +458,11 @@ int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled)
if (rtc->uie_rtctimer.enabled == enabled)
goto out;
+ if (rtc->uie_unsupported) {
+ err = -EINVAL;
+ goto out;
+ }
+
if (enabled) {
struct rtc_time tm;
ktime_t now, onesec;
diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c
index f04761e..afee0e8 100644
--- a/drivers/rtc/rtc-88pm860x.c
+++ b/drivers/rtc/rtc-88pm860x.c
@@ -376,6 +376,9 @@ static int __devinit pm860x_rtc_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&info->calib_work, calibrate_vrtc_work);
schedule_delayed_work(&info->calib_work, VRTC_CALIB_INTERVAL);
#endif /* VRTC_CALIBRATION */
+
+ device_init_wakeup(&pdev->dev, 1);
+
return 0;
out_rtc:
free_irq(info->irq, info);
@@ -401,10 +404,34 @@ static int __devexit pm860x_rtc_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int pm860x_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev))
+ chip->wakeup_flag |= 1 << PM8607_IRQ_RTC;
+ return 0;
+}
+static int pm860x_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev))
+ chip->wakeup_flag &= ~(1 << PM8607_IRQ_RTC);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm860x_rtc_pm_ops, pm860x_rtc_suspend, pm860x_rtc_resume);
+
static struct platform_driver pm860x_rtc_driver = {
.driver = {
.name = "88pm860x-rtc",
.owner = THIS_MODULE,
+ .pm = &pm860x_rtc_pm_ops,
},
.probe = pm860x_rtc_probe,
.remove = __devexit_p(pm860x_rtc_remove),
diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c
index e954a75..42f5f82 100644
--- a/drivers/rtc/rtc-mpc5121.c
+++ b/drivers/rtc/rtc-mpc5121.c
@@ -360,6 +360,8 @@ static int __devinit mpc5121_rtc_probe(struct platform_device *op)
&mpc5200_rtc_ops, THIS_MODULE);
}
+ rtc->rtc->uie_unsupported = 1;
+
if (IS_ERR(rtc->rtc)) {
err = PTR_ERR(rtc->rtc);
goto out_free_irq;
diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c
index 1300962..b2185f4 100644
--- a/drivers/rtc/rtc-mv.c
+++ b/drivers/rtc/rtc-mv.c
@@ -12,6 +12,7 @@
#include <linux/bcd.h>
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
#include <linux/delay.h>
#include <linux/gfp.h>
#include <linux/module.h>
@@ -294,11 +295,19 @@ static int __exit mv_rtc_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static struct of_device_id rtc_mv_of_match_table[] = {
+ { .compatible = "mrvl,orion-rtc", },
+ {}
+};
+#endif
+
static struct platform_driver mv_rtc_driver = {
.remove = __exit_p(mv_rtc_remove),
.driver = {
.name = "rtc-mv",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(rtc_mv_of_match_table),
},
};
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
index fa512ed..50a5c4a 100644
--- a/drivers/rtc/rtc-sa1100.c
+++ b/drivers/rtc/rtc-sa1100.c
@@ -33,9 +33,10 @@
#include <linux/of.h>
#include <linux/pm.h>
#include <linux/bitops.h>
+#include <linux/io.h>
#include <mach/hardware.h>
-#include <asm/irq.h>
+#include <mach/irqs.h>
#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP)
#include <mach/regs-rtc.h>
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index 168525a..231a1d8 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -21,6 +21,7 @@
#include <asm/chpid.h>
#include <asm/sclp.h>
#include <asm/setup.h>
+#include <asm/ctl_reg.h>
#include "sclp.h"
diff --git a/drivers/s390/cio/crw.c b/drivers/s390/cio/crw.c
index 425f741..d0a2dff 100644
--- a/drivers/s390/cio/crw.c
+++ b/drivers/s390/cio/crw.c
@@ -13,6 +13,7 @@
#include <linux/init.h>
#include <linux/wait.h>
#include <asm/crw.h>
+#include <asm/ctl_reg.h>
static DEFINE_MUTEX(crw_handler_mutex);
static crw_handler_t crw_handlers[NR_RSCS];
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 2a0dfcb..35c685c 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -1458,7 +1458,6 @@ int qdio_establish(struct qdio_initialize *init_data)
}
qdio_setup_ssqd_info(irq_ptr);
- DBF_EVENT("qib ac:%4x", irq_ptr->qib.ac);
qdio_detect_hsicq(irq_ptr);
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index 452989a..ecf12f0 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -311,7 +311,8 @@ void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr)
check_and_setup_qebsm(irq_ptr, qdioac, irq_ptr->ssqd_desc.sch_token);
process_ac_flags(irq_ptr, qdioac);
- DBF_EVENT("qdioac:%4x", qdioac);
+ DBF_EVENT("ac 1:%2x 2:%4x", qdioac, irq_ptr->ssqd_desc.qdioac2);
+ DBF_EVENT("3:%4x qib:%4x", irq_ptr->ssqd_desc.qdioac3, irq_ptr->qib.ac);
}
void qdio_release_memory(struct qdio_irq *irq_ptr)
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 12ae181..7e9a72e 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -42,10 +42,10 @@
#include <asm/reset.h>
#include <asm/airq.h>
#include <linux/atomic.h>
-#include <asm/system.h>
#include <asm/isc.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
+#include <asm/facility.h>
#include "ap_bus.h"
diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c
index 826157f..327657e 100644
--- a/drivers/sbus/char/flash.c
+++ b/drivers/sbus/char/flash.c
@@ -16,7 +16,6 @@
#include <linux/of.h>
#include <linux/of_device.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/io.h>
diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c
index 8d6e508..2236aea 100644
--- a/drivers/sbus/char/openprom.c
+++ b/drivers/sbus/char/openprom.c
@@ -40,7 +40,6 @@
#include <linux/fs.h>
#include <asm/oplib.h>
#include <asm/prom.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/openpromio.h>
#ifdef CONFIG_PCI
diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c
index 0b31658..a9e468c 100644
--- a/drivers/sbus/char/uctrl.c
+++ b/drivers/sbus/char/uctrl.c
@@ -19,7 +19,6 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/pgtable.h>
diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c
index f672491..a3adfb4 100644
--- a/drivers/scsi/53c700.c
+++ b/drivers/scsi/53c700.c
@@ -129,7 +129,6 @@
#include <linux/interrupt.h>
#include <linux/device.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/byteorder.h>
diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c
index f66c33b..d4da370 100644
--- a/drivers/scsi/BusLogic.c
+++ b/drivers/scsi/BusLogic.c
@@ -47,7 +47,6 @@
#include <asm/dma.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index a06e608..29684c8 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -619,6 +619,7 @@ config SCSI_ARCMSR
source "drivers/scsi/megaraid/Kconfig.megaraid"
source "drivers/scsi/mpt2sas/Kconfig"
+source "drivers/scsi/ufs/Kconfig"
config SCSI_HPTIOP
tristate "HighPoint RocketRAID 3xxx/4xxx Controller support"
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index ad24e06..8deedea 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -108,6 +108,7 @@ obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o
obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/
obj-$(CONFIG_MEGARAID_SAS) += megaraid/
obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas/
+obj-$(CONFIG_SCSI_UFSHCD) += ufs/
obj-$(CONFIG_SCSI_ACARD) += atp870u.o
obj-$(CONFIG_SCSI_SUNESP) += esp_scsi.o sun_esp.o
obj-$(CONFIG_SCSI_GDTH) += gdth.o
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
index bfd618a..374c4ed 100644
--- a/drivers/scsi/advansys.c
+++ b/drivers/scsi/advansys.c
@@ -41,7 +41,6 @@
#include <linux/firmware.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/dma.h>
#include <scsi/scsi_cmnd.h>
diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c
index f17c92c..19a3694 100644
--- a/drivers/scsi/aha152x.c
+++ b/drivers/scsi/aha152x.c
@@ -239,7 +239,6 @@
#include <asm/irq.h>
#include <linux/io.h>
#include <linux/blkdev.h>
-#include <asm/system.h>
#include <linux/completion.h>
#include <linux/errno.h>
#include <linux/string.h>
diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c
index ed119ce..ede91f3 100644
--- a/drivers/scsi/aha1542.c
+++ b/drivers/scsi/aha1542.c
@@ -42,7 +42,6 @@
#include <linux/slab.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <asm/io.h>
#include "scsi.h"
diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c
index 1c10b79..a3e6ed3 100644
--- a/drivers/scsi/aha1740.c
+++ b/drivers/scsi/aha1740.c
@@ -53,7 +53,6 @@
#include <linux/gfp.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <asm/io.h>
#include "scsi.h"
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c
index 7d48700..9328121 100644
--- a/drivers/scsi/aic7xxx/aic79xx_osm.c
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.c
@@ -341,10 +341,10 @@ MODULE_PARM_DESC(aic79xx,
" (0/256ms,1/128ms,2/64ms,3/32ms)\n"
" slowcrc Turn on the SLOWCRC bit (Rev B only)\n"
"\n"
-" Sample /etc/modprobe.conf line:\n"
-" Enable verbose logging\n"
-" Set tag depth on Controller 2/Target 2 to 10 tags\n"
-" Shorten the selection timeout to 128ms\n"
+" Sample modprobe configuration file:\n"
+" # Enable verbose logging\n"
+" # Set tag depth on Controller 2/Target 2 to 10 tags\n"
+" # Shorten the selection timeout to 128ms\n"
"\n"
" options aic79xx 'aic79xx=verbose.tag_info:{{}.{}.{..10}}.seltime:1'\n"
);
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c
index c6251bb..5a477cd 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_osm.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -360,10 +360,10 @@ MODULE_PARM_DESC(aic7xxx,
" seltime:<int> Selection Timeout\n"
" (0/256ms,1/128ms,2/64ms,3/32ms)\n"
"\n"
-" Sample /etc/modprobe.conf line:\n"
-" Toggle EISA/VLB probing\n"
-" Set tag depth on Controller 1/Target 1 to 10 tags\n"
-" Shorten the selection timeout to 128ms\n"
+" Sample modprobe configuration file:\n"
+" # Toggle EISA/VLB probing\n"
+" # Set tag depth on Controller 1/Target 1 to 10 tags\n"
+" # Shorten the selection timeout to 128ms\n"
"\n"
" options aic7xxx 'aic7xxx=probe_eisa_vl.tag_info:{{}.{.10}}.seltime:1'\n"
);
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 2fe9e90..cbde1dc 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -61,7 +61,6 @@
#include <linux/aer.h>
#include <asm/dma.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi.h>
diff --git a/drivers/scsi/arm/acornscsi.c b/drivers/scsi/arm/acornscsi.c
index c454e44..b330438 100644
--- a/drivers/scsi/arm/acornscsi.c
+++ b/drivers/scsi/arm/acornscsi.c
@@ -138,7 +138,6 @@
#include <linux/stringify.h>
#include <linux/io.h>
-#include <asm/system.h>
#include <asm/ecard.h>
#include "../scsi.h"
diff --git a/drivers/scsi/arm/arxescsi.c b/drivers/scsi/arm/arxescsi.c
index a750aa7..2a28b4a 100644
--- a/drivers/scsi/arm/arxescsi.c
+++ b/drivers/scsi/arm/arxescsi.c
@@ -305,7 +305,7 @@ arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id)
info->base = base;
info->info.scsi.io_base = base + 0x2000;
- info->info.scsi.irq = NO_IRQ;
+ info->info.scsi.irq = 0;
info->info.scsi.dma = NO_DMA;
info->info.scsi.io_shift = 5;
info->info.ifcfg.clockrate = 24; /* MHz */
diff --git a/drivers/scsi/arm/cumana_1.c b/drivers/scsi/arm/cumana_1.c
index a3398fe..c3b99c9 100644
--- a/drivers/scsi/arm/cumana_1.c
+++ b/drivers/scsi/arm/cumana_1.c
@@ -12,7 +12,6 @@
#include <asm/ecard.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "../scsi.h"
#include <scsi/scsi_host.h>
diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c
index e85c40b..6206a66 100644
--- a/drivers/scsi/arm/fas216.c
+++ b/drivers/scsi/arm/fas216.c
@@ -2176,7 +2176,7 @@ static void fas216_done(FAS216_Info *info, unsigned int result)
fn = (void (*)(FAS216_Info *, struct scsi_cmnd *, unsigned int))SCpnt->host_scribble;
fn(info, SCpnt, result);
- if (info->scsi.irq != NO_IRQ) {
+ if (info->scsi.irq) {
spin_lock_irqsave(&info->host_lock, flags);
if (info->scsi.phase == PHASE_IDLE)
fas216_kick(info);
@@ -2276,7 +2276,7 @@ static int fas216_noqueue_command_lck(struct scsi_cmnd *SCpnt,
* We should only be using this if we don't have an interrupt.
* Provide some "incentive" to use the queueing code.
*/
- BUG_ON(info->scsi.irq != NO_IRQ);
+ BUG_ON(info->scsi.irq);
info->internal_done = 0;
fas216_queue_command_lck(SCpnt, fas216_internal_done);
diff --git a/drivers/scsi/arm/fas216.h b/drivers/scsi/arm/fas216.h
index 84b7127..df2e1b3 100644
--- a/drivers/scsi/arm/fas216.h
+++ b/drivers/scsi/arm/fas216.h
@@ -12,10 +12,6 @@
#ifndef FAS216_H
#define FAS216_H
-#ifndef NO_IRQ
-#define NO_IRQ 255
-#endif
-
#include <scsi/scsi_eh.h>
#include "queue.h"
diff --git a/drivers/scsi/arm/oak.c b/drivers/scsi/arm/oak.c
index 849cdf8..d25f944 100644
--- a/drivers/scsi/arm/oak.c
+++ b/drivers/scsi/arm/oak.c
@@ -13,7 +13,6 @@
#include <asm/ecard.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "../scsi.h"
#include <scsi/scsi_host.h>
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c
index 7e6eca4..68ce085 100644
--- a/drivers/scsi/atp870u.c
+++ b/drivers/scsi/atp870u.c
@@ -30,7 +30,6 @@
#include <linux/blkdev.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <scsi/scsi.h>
@@ -2583,7 +2582,7 @@ static int atp870u_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
* this than via the PCI device table
*/
if (ent->device == PCI_DEVICE_ID_ARTOP_AEC7610) {
- error = pci_read_config_byte(pdev, PCI_CLASS_REVISION, &atpdev->chip_ver);
+ atpdev->chip_ver = pdev->revision;
if (atpdev->chip_ver < 2)
goto err_eio;
}
@@ -2602,7 +2601,7 @@ static int atp870u_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
base_io &= 0xfffffff8;
if ((ent->device == ATP880_DEVID1)||(ent->device == ATP880_DEVID2)) {
- error = pci_read_config_byte(pdev, PCI_CLASS_REVISION, &atpdev->chip_ver);
+ atpdev->chip_ver = pdev->revision;
pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80);//JCC082803
host_id = inb(base_io + 0x39);
diff --git a/drivers/scsi/bfa/bfa.h b/drivers/scsi/bfa/bfa.h
index a796de9..4ad7e36 100644
--- a/drivers/scsi/bfa/bfa.h
+++ b/drivers/scsi/bfa/bfa.h
@@ -225,9 +225,9 @@ struct bfa_faa_args_s {
};
struct bfa_iocfc_s {
+ bfa_fsm_t fsm;
struct bfa_s *bfa;
struct bfa_iocfc_cfg_s cfg;
- int action;
u32 req_cq_pi[BFI_IOC_MAX_CQS];
u32 rsp_cq_ci[BFI_IOC_MAX_CQS];
u8 hw_qid[BFI_IOC_MAX_CQS];
@@ -236,7 +236,9 @@ struct bfa_iocfc_s {
struct bfa_cb_qe_s dis_hcb_qe;
struct bfa_cb_qe_s en_hcb_qe;
struct bfa_cb_qe_s stats_hcb_qe;
- bfa_boolean_t cfgdone;
+ bfa_boolean_t submod_enabled;
+ bfa_boolean_t cb_reqd; /* Driver call back reqd */
+ bfa_status_t op_status; /* Status of bfa iocfc op */
struct bfa_dma_s cfg_info;
struct bfi_iocfc_cfg_s *cfginfo;
@@ -341,8 +343,6 @@ void bfa_hwct_msix_getvecs(struct bfa_s *bfa, u32 *vecmap, u32 *nvecs,
void bfa_hwct_msix_get_rme_range(struct bfa_s *bfa, u32 *start,
u32 *end);
void bfa_iocfc_get_bootwwns(struct bfa_s *bfa, u8 *nwwns, wwn_t *wwns);
-wwn_t bfa_iocfc_get_pwwn(struct bfa_s *bfa);
-wwn_t bfa_iocfc_get_nwwn(struct bfa_s *bfa);
int bfa_iocfc_get_pbc_vports(struct bfa_s *bfa,
struct bfi_pbc_vport_s *pbc_vport);
@@ -428,7 +428,6 @@ bfa_status_t bfa_iocfc_israttr_set(struct bfa_s *bfa,
void bfa_iocfc_enable(struct bfa_s *bfa);
void bfa_iocfc_disable(struct bfa_s *bfa);
-void bfa_iocfc_cb_dconf_modinit(struct bfa_s *bfa, bfa_status_t status);
#define bfa_timer_start(_bfa, _timer, _timercb, _arg, _timeout) \
bfa_timer_begin(&(_bfa)->timer_mod, _timer, _timercb, _arg, _timeout)
diff --git a/drivers/scsi/bfa/bfa_core.c b/drivers/scsi/bfa/bfa_core.c
index 4bd546b..456e576 100644
--- a/drivers/scsi/bfa/bfa_core.c
+++ b/drivers/scsi/bfa/bfa_core.c
@@ -200,13 +200,431 @@ enum {
#define DEF_CFG_NUM_SBOOT_LUNS 16
/*
+ * IOCFC state machine definitions/declarations
+ */
+bfa_fsm_state_decl(bfa_iocfc, stopped, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, initing, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, dconf_read, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, init_cfg_wait,
+ struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, init_cfg_done,
+ struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, operational,
+ struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, dconf_write,
+ struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, stopping, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, enabling, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, cfg_wait, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, disabling, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, disabled, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, failed, struct bfa_iocfc_s, enum iocfc_event);
+bfa_fsm_state_decl(bfa_iocfc, init_failed,
+ struct bfa_iocfc_s, enum iocfc_event);
+
+/*
* forward declaration for IOC FC functions
*/
+static void bfa_iocfc_start_submod(struct bfa_s *bfa);
+static void bfa_iocfc_disable_submod(struct bfa_s *bfa);
+static void bfa_iocfc_send_cfg(void *bfa_arg);
static void bfa_iocfc_enable_cbfn(void *bfa_arg, enum bfa_status status);
static void bfa_iocfc_disable_cbfn(void *bfa_arg);
static void bfa_iocfc_hbfail_cbfn(void *bfa_arg);
static void bfa_iocfc_reset_cbfn(void *bfa_arg);
static struct bfa_ioc_cbfn_s bfa_iocfc_cbfn;
+static void bfa_iocfc_init_cb(void *bfa_arg, bfa_boolean_t complete);
+static void bfa_iocfc_stop_cb(void *bfa_arg, bfa_boolean_t compl);
+static void bfa_iocfc_enable_cb(void *bfa_arg, bfa_boolean_t compl);
+static void bfa_iocfc_disable_cb(void *bfa_arg, bfa_boolean_t compl);
+
+static void
+bfa_iocfc_sm_stopped_entry(struct bfa_iocfc_s *iocfc)
+{
+}
+
+static void
+bfa_iocfc_sm_stopped(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_INIT:
+ case IOCFC_E_ENABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_initing);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_initing_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_ioc_enable(&iocfc->bfa->ioc);
+}
+
+static void
+bfa_iocfc_sm_initing(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_IOC_ENABLED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_read);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_failed);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_dconf_read_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_dconf_modinit(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_dconf_read(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_DCONF_DONE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_cfg_wait);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_failed);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_init_cfg_wait_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_iocfc_send_cfg(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_init_cfg_wait(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_CFG_DONE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_cfg_done);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_failed);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_init_cfg_done_entry(struct bfa_iocfc_s *iocfc)
+{
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.init_hcb_qe,
+ bfa_iocfc_init_cb, iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_init_cfg_done(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_START:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_operational);
+ break;
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping);
+ break;
+ case IOCFC_E_DISABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_operational_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_fcport_init(iocfc->bfa);
+ bfa_iocfc_start_submod(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_operational(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write);
+ break;
+ case IOCFC_E_DISABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_dconf_write_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_dconf_modexit(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_dconf_write(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_DCONF_DONE:
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_stopping_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_ioc_disable(&iocfc->bfa->ioc);
+}
+
+static void
+bfa_iocfc_sm_stopping(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_IOC_DISABLED:
+ bfa_isr_disable(iocfc->bfa);
+ bfa_iocfc_disable_submod(iocfc->bfa);
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopped);
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.stop_hcb_qe,
+ bfa_iocfc_stop_cb, iocfc->bfa);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_enabling_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_ioc_enable(&iocfc->bfa->ioc);
+}
+
+static void
+bfa_iocfc_sm_enabling(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_IOC_ENABLED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_cfg_wait);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed);
+
+ if (iocfc->bfa->iocfc.cb_reqd == BFA_FALSE)
+ break;
+
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_FAILED;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.en_hcb_qe,
+ bfa_iocfc_enable_cb, iocfc->bfa);
+ iocfc->bfa->iocfc.cb_reqd = BFA_FALSE;
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_cfg_wait_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_iocfc_send_cfg(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_cfg_wait(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_CFG_DONE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_operational);
+ if (iocfc->bfa->iocfc.cb_reqd == BFA_FALSE)
+ break;
+
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.en_hcb_qe,
+ bfa_iocfc_enable_cb, iocfc->bfa);
+ iocfc->bfa->iocfc.cb_reqd = BFA_FALSE;
+ break;
+ case IOCFC_E_IOC_FAILED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed);
+ if (iocfc->bfa->iocfc.cb_reqd == BFA_FALSE)
+ break;
+
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_FAILED;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.en_hcb_qe,
+ bfa_iocfc_enable_cb, iocfc->bfa);
+ iocfc->bfa->iocfc.cb_reqd = BFA_FALSE;
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_disabling_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_ioc_disable(&iocfc->bfa->ioc);
+}
+
+static void
+bfa_iocfc_sm_disabling(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_IOC_DISABLED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabled);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_disabled_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_isr_disable(iocfc->bfa);
+ bfa_iocfc_disable_submod(iocfc->bfa);
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.dis_hcb_qe,
+ bfa_iocfc_disable_cb, iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_disabled(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write);
+ break;
+ case IOCFC_E_ENABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_enabling);
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_failed_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_isr_disable(iocfc->bfa);
+ bfa_iocfc_disable_submod(iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_failed(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write);
+ break;
+ case IOCFC_E_DISABLE:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling);
+ break;
+ case IOCFC_E_IOC_ENABLED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_cfg_wait);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
+
+static void
+bfa_iocfc_sm_init_failed_entry(struct bfa_iocfc_s *iocfc)
+{
+ bfa_isr_disable(iocfc->bfa);
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_FAILED;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.init_hcb_qe,
+ bfa_iocfc_init_cb, iocfc->bfa);
+}
+
+static void
+bfa_iocfc_sm_init_failed(struct bfa_iocfc_s *iocfc, enum iocfc_event event)
+{
+ bfa_trc(iocfc->bfa, event);
+
+ switch (event) {
+ case IOCFC_E_STOP:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping);
+ break;
+ case IOCFC_E_DISABLE:
+ bfa_ioc_disable(&iocfc->bfa->ioc);
+ break;
+ case IOCFC_E_IOC_ENABLED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_read);
+ break;
+ case IOCFC_E_IOC_DISABLED:
+ bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopped);
+ iocfc->bfa->iocfc.op_status = BFA_STATUS_OK;
+ bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.dis_hcb_qe,
+ bfa_iocfc_disable_cb, iocfc->bfa);
+ break;
+ case IOCFC_E_IOC_FAILED:
+ break;
+ default:
+ bfa_sm_fault(iocfc->bfa, event);
+ break;
+ }
+}
/*
* BFA Interrupt handling functions
@@ -231,16 +649,19 @@ bfa_reqq_resume(struct bfa_s *bfa, int qid)
}
}
-static inline void
+bfa_boolean_t
bfa_isr_rspq(struct bfa_s *bfa, int qid)
{
struct bfi_msg_s *m;
u32 pi, ci;
struct list_head *waitq;
+ bfa_boolean_t ret;
ci = bfa_rspq_ci(bfa, qid);
pi = bfa_rspq_pi(bfa, qid);
+ ret = (ci != pi);
+
while (ci != pi) {
m = bfa_rspq_elem(bfa, qid, ci);
WARN_ON(m->mhdr.msg_class >= BFI_MC_MAX);
@@ -260,6 +681,8 @@ bfa_isr_rspq(struct bfa_s *bfa, int qid)
waitq = bfa_reqq(bfa, qid);
if (!list_empty(waitq))
bfa_reqq_resume(bfa, qid);
+
+ return ret;
}
static inline void
@@ -320,6 +743,7 @@ bfa_intx(struct bfa_s *bfa)
{
u32 intr, qintr;
int queue;
+ bfa_boolean_t rspq_comp = BFA_FALSE;
intr = readl(bfa->iocfc.bfa_regs.intr_status);
@@ -332,11 +756,12 @@ bfa_intx(struct bfa_s *bfa)
*/
if (bfa->queue_process) {
for (queue = 0; queue < BFI_IOC_MAX_CQS; queue++)
- bfa_isr_rspq(bfa, queue);
+ if (bfa_isr_rspq(bfa, queue))
+ rspq_comp = BFA_TRUE;
}
if (!intr)
- return BFA_TRUE;
+ return (qintr | rspq_comp) ? BFA_TRUE : BFA_FALSE;
/*
* CPE completion queue interrupt
@@ -525,11 +950,9 @@ bfa_iocfc_send_cfg(void *bfa_arg)
* Enable interrupt coalescing if it is driver init path
* and not ioc disable/enable path.
*/
- if (!iocfc->cfgdone)
+ if (bfa_fsm_cmp_state(iocfc, bfa_iocfc_sm_init_cfg_wait))
cfg_info->intr_attr.coalesce = BFA_TRUE;
- iocfc->cfgdone = BFA_FALSE;
-
/*
* dma map IOC configuration itself
*/
@@ -549,8 +972,6 @@ bfa_iocfc_init_mem(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
bfa->bfad = bfad;
iocfc->bfa = bfa;
- iocfc->action = BFA_IOCFC_ACT_NONE;
-
iocfc->cfg = *cfg;
/*
@@ -683,6 +1104,8 @@ bfa_iocfc_start_submod(struct bfa_s *bfa)
for (i = 0; hal_mods[i]; i++)
hal_mods[i]->start(bfa);
+
+ bfa->iocfc.submod_enabled = BFA_TRUE;
}
/*
@@ -693,8 +1116,13 @@ bfa_iocfc_disable_submod(struct bfa_s *bfa)
{
int i;
+ if (bfa->iocfc.submod_enabled == BFA_FALSE)
+ return;
+
for (i = 0; hal_mods[i]; i++)
hal_mods[i]->iocdisable(bfa);
+
+ bfa->iocfc.submod_enabled = BFA_FALSE;
}
static void
@@ -702,15 +1130,8 @@ bfa_iocfc_init_cb(void *bfa_arg, bfa_boolean_t complete)
{
struct bfa_s *bfa = bfa_arg;
- if (complete) {
- if (bfa->iocfc.cfgdone && BFA_DCONF_MOD(bfa)->flashdone)
- bfa_cb_init(bfa->bfad, BFA_STATUS_OK);
- else
- bfa_cb_init(bfa->bfad, BFA_STATUS_FAILED);
- } else {
- if (bfa->iocfc.cfgdone)
- bfa->iocfc.action = BFA_IOCFC_ACT_NONE;
- }
+ if (complete)
+ bfa_cb_init(bfa->bfad, bfa->iocfc.op_status);
}
static void
@@ -721,8 +1142,6 @@ bfa_iocfc_stop_cb(void *bfa_arg, bfa_boolean_t compl)
if (compl)
complete(&bfad->comp);
- else
- bfa->iocfc.action = BFA_IOCFC_ACT_NONE;
}
static void
@@ -794,8 +1213,6 @@ bfa_iocfc_cfgrsp(struct bfa_s *bfa)
fwcfg->num_uf_bufs = be16_to_cpu(fwcfg->num_uf_bufs);
fwcfg->num_rports = be16_to_cpu(fwcfg->num_rports);
- iocfc->cfgdone = BFA_TRUE;
-
/*
* configure queue register offsets as learnt from firmware
*/
@@ -811,22 +1228,13 @@ bfa_iocfc_cfgrsp(struct bfa_s *bfa)
*/
bfa_msix_queue_install(bfa);
- /*
- * Configuration is complete - initialize/start submodules
- */
- bfa_fcport_init(bfa);
-
- if (iocfc->action == BFA_IOCFC_ACT_INIT) {
- if (BFA_DCONF_MOD(bfa)->flashdone == BFA_TRUE)
- bfa_cb_queue(bfa, &iocfc->init_hcb_qe,
- bfa_iocfc_init_cb, bfa);
- } else {
- if (bfa->iocfc.action == BFA_IOCFC_ACT_ENABLE)
- bfa_cb_queue(bfa, &bfa->iocfc.en_hcb_qe,
- bfa_iocfc_enable_cb, bfa);
- bfa_iocfc_start_submod(bfa);
+ if (bfa->iocfc.cfgrsp->pbc_cfg.pbc_pwwn != 0) {
+ bfa->ioc.attr->pwwn = bfa->iocfc.cfgrsp->pbc_cfg.pbc_pwwn;
+ bfa->ioc.attr->nwwn = bfa->iocfc.cfgrsp->pbc_cfg.pbc_nwwn;
+ bfa_fsm_send_event(iocfc, IOCFC_E_CFG_DONE);
}
}
+
void
bfa_iocfc_reset_queues(struct bfa_s *bfa)
{
@@ -840,6 +1248,23 @@ bfa_iocfc_reset_queues(struct bfa_s *bfa)
}
}
+/*
+ * Process FAA pwwn msg from fw.
+ */
+static void
+bfa_iocfc_process_faa_addr(struct bfa_s *bfa, struct bfi_faa_addr_msg_s *msg)
+{
+ struct bfa_iocfc_s *iocfc = &bfa->iocfc;
+ struct bfi_iocfc_cfgrsp_s *cfgrsp = iocfc->cfgrsp;
+
+ cfgrsp->pbc_cfg.pbc_pwwn = msg->pwwn;
+ cfgrsp->pbc_cfg.pbc_nwwn = msg->nwwn;
+
+ bfa->ioc.attr->pwwn = msg->pwwn;
+ bfa->ioc.attr->nwwn = msg->nwwn;
+ bfa_fsm_send_event(iocfc, IOCFC_E_CFG_DONE);
+}
+
/* Fabric Assigned Address specific functions */
/*
@@ -855,84 +1280,13 @@ bfa_faa_validate_request(struct bfa_s *bfa)
if ((ioc_type != BFA_IOC_TYPE_FC) || bfa_mfg_is_mezz(card_type))
return BFA_STATUS_FEATURE_NOT_SUPPORTED;
} else {
- if (!bfa_ioc_is_acq_addr(&bfa->ioc))
- return BFA_STATUS_IOC_NON_OP;
+ return BFA_STATUS_IOC_NON_OP;
}
return BFA_STATUS_OK;
}
bfa_status_t
-bfa_faa_enable(struct bfa_s *bfa, bfa_cb_iocfc_t cbfn, void *cbarg)
-{
- struct bfi_faa_en_dis_s faa_enable_req;
- struct bfa_iocfc_s *iocfc = &bfa->iocfc;
- bfa_status_t status;
-
- iocfc->faa_args.faa_cb.faa_cbfn = cbfn;
- iocfc->faa_args.faa_cb.faa_cbarg = cbarg;
-
- status = bfa_faa_validate_request(bfa);
- if (status != BFA_STATUS_OK)
- return status;
-
- if (iocfc->faa_args.busy == BFA_TRUE)
- return BFA_STATUS_DEVBUSY;
-
- if (iocfc->faa_args.faa_state == BFA_FAA_ENABLED)
- return BFA_STATUS_FAA_ENABLED;
-
- if (bfa_fcport_is_trunk_enabled(bfa))
- return BFA_STATUS_ERROR_TRUNK_ENABLED;
-
- bfa_fcport_cfg_faa(bfa, BFA_FAA_ENABLED);
- iocfc->faa_args.busy = BFA_TRUE;
-
- memset(&faa_enable_req, 0, sizeof(struct bfi_faa_en_dis_s));
- bfi_h2i_set(faa_enable_req.mh, BFI_MC_IOCFC,
- BFI_IOCFC_H2I_FAA_ENABLE_REQ, bfa_fn_lpu(bfa));
-
- bfa_ioc_mbox_send(&bfa->ioc, &faa_enable_req,
- sizeof(struct bfi_faa_en_dis_s));
-
- return BFA_STATUS_OK;
-}
-
-bfa_status_t
-bfa_faa_disable(struct bfa_s *bfa, bfa_cb_iocfc_t cbfn,
- void *cbarg)
-{
- struct bfi_faa_en_dis_s faa_disable_req;
- struct bfa_iocfc_s *iocfc = &bfa->iocfc;
- bfa_status_t status;
-
- iocfc->faa_args.faa_cb.faa_cbfn = cbfn;
- iocfc->faa_args.faa_cb.faa_cbarg = cbarg;
-
- status = bfa_faa_validate_request(bfa);
- if (status != BFA_STATUS_OK)
- return status;
-
- if (iocfc->faa_args.busy == BFA_TRUE)
- return BFA_STATUS_DEVBUSY;
-
- if (iocfc->faa_args.faa_state == BFA_FAA_DISABLED)
- return BFA_STATUS_FAA_DISABLED;
-
- bfa_fcport_cfg_faa(bfa, BFA_FAA_DISABLED);
- iocfc->faa_args.busy = BFA_TRUE;
-
- memset(&faa_disable_req, 0, sizeof(struct bfi_faa_en_dis_s));
- bfi_h2i_set(faa_disable_req.mh, BFI_MC_IOCFC,
- BFI_IOCFC_H2I_FAA_DISABLE_REQ, bfa_fn_lpu(bfa));
-
- bfa_ioc_mbox_send(&bfa->ioc, &faa_disable_req,
- sizeof(struct bfi_faa_en_dis_s));
-
- return BFA_STATUS_OK;
-}
-
-bfa_status_t
bfa_faa_query(struct bfa_s *bfa, struct bfa_faa_attr_s *attr,
bfa_cb_iocfc_t cbfn, void *cbarg)
{
@@ -963,38 +1317,6 @@ bfa_faa_query(struct bfa_s *bfa, struct bfa_faa_attr_s *attr,
}
/*
- * FAA enable response
- */
-static void
-bfa_faa_enable_reply(struct bfa_iocfc_s *iocfc,
- struct bfi_faa_en_dis_rsp_s *rsp)
-{
- void *cbarg = iocfc->faa_args.faa_cb.faa_cbarg;
- bfa_status_t status = rsp->status;
-
- WARN_ON(!iocfc->faa_args.faa_cb.faa_cbfn);
-
- iocfc->faa_args.faa_cb.faa_cbfn(cbarg, status);
- iocfc->faa_args.busy = BFA_FALSE;
-}
-
-/*
- * FAA disable response
- */
-static void
-bfa_faa_disable_reply(struct bfa_iocfc_s *iocfc,
- struct bfi_faa_en_dis_rsp_s *rsp)
-{
- void *cbarg = iocfc->faa_args.faa_cb.faa_cbarg;
- bfa_status_t status = rsp->status;
-
- WARN_ON(!iocfc->faa_args.faa_cb.faa_cbfn);
-
- iocfc->faa_args.faa_cb.faa_cbfn(cbarg, status);
- iocfc->faa_args.busy = BFA_FALSE;
-}
-
-/*
* FAA query response
*/
static void
@@ -1023,25 +1345,10 @@ bfa_iocfc_enable_cbfn(void *bfa_arg, enum bfa_status status)
{
struct bfa_s *bfa = bfa_arg;
- if (status == BFA_STATUS_FAA_ACQ_ADDR) {
- bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe,
- bfa_iocfc_init_cb, bfa);
- return;
- }
-
- if (status != BFA_STATUS_OK) {
- bfa_isr_disable(bfa);
- if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT)
- bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe,
- bfa_iocfc_init_cb, bfa);
- else if (bfa->iocfc.action == BFA_IOCFC_ACT_ENABLE)
- bfa_cb_queue(bfa, &bfa->iocfc.en_hcb_qe,
- bfa_iocfc_enable_cb, bfa);
- return;
- }
-
- bfa_iocfc_send_cfg(bfa);
- bfa_dconf_modinit(bfa);
+ if (status == BFA_STATUS_OK)
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_IOC_ENABLED);
+ else
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_IOC_FAILED);
}
/*
@@ -1052,17 +1359,7 @@ bfa_iocfc_disable_cbfn(void *bfa_arg)
{
struct bfa_s *bfa = bfa_arg;
- bfa_isr_disable(bfa);
- bfa_iocfc_disable_submod(bfa);
-
- if (bfa->iocfc.action == BFA_IOCFC_ACT_STOP)
- bfa_cb_queue(bfa, &bfa->iocfc.stop_hcb_qe, bfa_iocfc_stop_cb,
- bfa);
- else {
- WARN_ON(bfa->iocfc.action != BFA_IOCFC_ACT_DISABLE);
- bfa_cb_queue(bfa, &bfa->iocfc.dis_hcb_qe, bfa_iocfc_disable_cb,
- bfa);
- }
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_IOC_DISABLED);
}
/*
@@ -1074,13 +1371,7 @@ bfa_iocfc_hbfail_cbfn(void *bfa_arg)
struct bfa_s *bfa = bfa_arg;
bfa->queue_process = BFA_FALSE;
-
- bfa_isr_disable(bfa);
- bfa_iocfc_disable_submod(bfa);
-
- if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT)
- bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe, bfa_iocfc_init_cb,
- bfa);
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_IOC_FAILED);
}
/*
@@ -1095,7 +1386,6 @@ bfa_iocfc_reset_cbfn(void *bfa_arg)
bfa_isr_enable(bfa);
}
-
/*
* Query IOC memory requirement information.
*/
@@ -1171,6 +1461,12 @@ bfa_iocfc_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
INIT_LIST_HEAD(&bfa->comp_q);
for (i = 0; i < BFI_IOC_MAX_CQS; i++)
INIT_LIST_HEAD(&bfa->reqq_waitq[i]);
+
+ bfa->iocfc.cb_reqd = BFA_FALSE;
+ bfa->iocfc.op_status = BFA_STATUS_OK;
+ bfa->iocfc.submod_enabled = BFA_FALSE;
+
+ bfa_fsm_set_state(&bfa->iocfc, bfa_iocfc_sm_stopped);
}
/*
@@ -1179,8 +1475,7 @@ bfa_iocfc_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
void
bfa_iocfc_init(struct bfa_s *bfa)
{
- bfa->iocfc.action = BFA_IOCFC_ACT_INIT;
- bfa_ioc_enable(&bfa->ioc);
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_INIT);
}
/*
@@ -1190,8 +1485,7 @@ bfa_iocfc_init(struct bfa_s *bfa)
void
bfa_iocfc_start(struct bfa_s *bfa)
{
- if (bfa->iocfc.cfgdone)
- bfa_iocfc_start_submod(bfa);
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_START);
}
/*
@@ -1201,12 +1495,8 @@ bfa_iocfc_start(struct bfa_s *bfa)
void
bfa_iocfc_stop(struct bfa_s *bfa)
{
- bfa->iocfc.action = BFA_IOCFC_ACT_STOP;
-
bfa->queue_process = BFA_FALSE;
- bfa_dconf_modexit(bfa);
- if (BFA_DCONF_MOD(bfa)->flashdone == BFA_TRUE)
- bfa_ioc_disable(&bfa->ioc);
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_STOP);
}
void
@@ -1226,13 +1516,9 @@ bfa_iocfc_isr(void *bfaarg, struct bfi_mbmsg_s *m)
case BFI_IOCFC_I2H_UPDATEQ_RSP:
iocfc->updateq_cbfn(iocfc->updateq_cbarg, BFA_STATUS_OK);
break;
- case BFI_IOCFC_I2H_FAA_ENABLE_RSP:
- bfa_faa_enable_reply(iocfc,
- (struct bfi_faa_en_dis_rsp_s *)msg);
- break;
- case BFI_IOCFC_I2H_FAA_DISABLE_RSP:
- bfa_faa_disable_reply(iocfc,
- (struct bfi_faa_en_dis_rsp_s *)msg);
+ case BFI_IOCFC_I2H_ADDR_MSG:
+ bfa_iocfc_process_faa_addr(bfa,
+ (struct bfi_faa_addr_msg_s *)msg);
break;
case BFI_IOCFC_I2H_FAA_QUERY_RSP:
bfa_faa_query_reply(iocfc, (bfi_faa_query_rsp_t *)msg);
@@ -1306,8 +1592,8 @@ bfa_iocfc_enable(struct bfa_s *bfa)
{
bfa_plog_str(bfa->plog, BFA_PL_MID_HAL, BFA_PL_EID_MISC, 0,
"IOC Enable");
- bfa->iocfc.action = BFA_IOCFC_ACT_ENABLE;
- bfa_ioc_enable(&bfa->ioc);
+ bfa->iocfc.cb_reqd = BFA_TRUE;
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_ENABLE);
}
void
@@ -1315,17 +1601,16 @@ bfa_iocfc_disable(struct bfa_s *bfa)
{
bfa_plog_str(bfa->plog, BFA_PL_MID_HAL, BFA_PL_EID_MISC, 0,
"IOC Disable");
- bfa->iocfc.action = BFA_IOCFC_ACT_DISABLE;
bfa->queue_process = BFA_FALSE;
- bfa_ioc_disable(&bfa->ioc);
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_DISABLE);
}
-
bfa_boolean_t
bfa_iocfc_is_operational(struct bfa_s *bfa)
{
- return bfa_ioc_is_operational(&bfa->ioc) && bfa->iocfc.cfgdone;
+ return bfa_ioc_is_operational(&bfa->ioc) &&
+ bfa_fsm_cmp_state(&bfa->iocfc, bfa_iocfc_sm_operational);
}
/*
@@ -1567,16 +1852,6 @@ bfa_comp_free(struct bfa_s *bfa, struct list_head *comp_q)
}
}
-void
-bfa_iocfc_cb_dconf_modinit(struct bfa_s *bfa, bfa_status_t status)
-{
- if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT) {
- if (bfa->iocfc.cfgdone == BFA_TRUE)
- bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe,
- bfa_iocfc_init_cb, bfa);
- }
-}
-
/*
* Return the list of PCI vendor/device id lists supported by this
* BFA instance.
diff --git a/drivers/scsi/bfa/bfa_defs_svc.h b/drivers/scsi/bfa/bfa_defs_svc.h
index cb07c62..36756ce 100644
--- a/drivers/scsi/bfa/bfa_defs_svc.h
+++ b/drivers/scsi/bfa/bfa_defs_svc.h
@@ -52,7 +52,7 @@ struct bfa_iocfc_fwcfg_s {
u16 num_uf_bufs; /* unsolicited recv buffers */
u8 num_cqs;
u8 fw_tick_res; /* FW clock resolution in ms */
- u8 rsvd[2];
+ u8 rsvd[6];
};
#pragma pack()
diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c
index d4f951f..5d2a130 100644
--- a/drivers/scsi/bfa/bfa_fcs_lport.c
+++ b/drivers/scsi/bfa/bfa_fcs_lport.c
@@ -5717,6 +5717,8 @@ bfa_fcs_vport_free(struct bfa_fcs_vport_s *vport)
if (vport_drv->comp_del)
complete(vport_drv->comp_del);
+ else
+ kfree(vport_drv);
bfa_lps_delete(vport->lps);
}
diff --git a/drivers/scsi/bfa/bfa_fcs_rport.c b/drivers/scsi/bfa/bfa_fcs_rport.c
index 52628d5..fe0463a 100644
--- a/drivers/scsi/bfa/bfa_fcs_rport.c
+++ b/drivers/scsi/bfa/bfa_fcs_rport.c
@@ -2169,7 +2169,10 @@ bfa_fcs_rport_update(struct bfa_fcs_rport_s *rport, struct fc_logi_s *plogi)
* - MAX receive frame size
*/
rport->cisc = plogi->csp.cisc;
- rport->maxfrsize = be16_to_cpu(plogi->class3.rxsz);
+ if (be16_to_cpu(plogi->class3.rxsz) < be16_to_cpu(plogi->csp.rxsz))
+ rport->maxfrsize = be16_to_cpu(plogi->class3.rxsz);
+ else
+ rport->maxfrsize = be16_to_cpu(plogi->csp.rxsz);
bfa_trc(port->fcs, be16_to_cpu(plogi->csp.bbcred));
bfa_trc(port->fcs, port->fabric->bb_credit);
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index eca7ab7..14e6284 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -88,7 +88,6 @@ static void bfa_ioc_hb_monitor(struct bfa_ioc_s *ioc);
static void bfa_ioc_mbox_poll(struct bfa_ioc_s *ioc);
static void bfa_ioc_mbox_flush(struct bfa_ioc_s *ioc);
static void bfa_ioc_recover(struct bfa_ioc_s *ioc);
-static void bfa_ioc_check_attr_wwns(struct bfa_ioc_s *ioc);
static void bfa_ioc_event_notify(struct bfa_ioc_s *ioc ,
enum bfa_ioc_event_e event);
static void bfa_ioc_disable_comp(struct bfa_ioc_s *ioc);
@@ -97,7 +96,6 @@ static void bfa_ioc_debug_save_ftrc(struct bfa_ioc_s *ioc);
static void bfa_ioc_fail_notify(struct bfa_ioc_s *ioc);
static void bfa_ioc_pf_fwmismatch(struct bfa_ioc_s *ioc);
-
/*
* IOC state machine definitions/declarations
*/
@@ -114,7 +112,6 @@ enum ioc_event {
IOC_E_HWERROR = 10, /* hardware error interrupt */
IOC_E_TIMEOUT = 11, /* timeout */
IOC_E_HWFAILED = 12, /* PCI mapping failure notice */
- IOC_E_FWRSP_ACQ_ADDR = 13, /* Acquiring address */
};
bfa_fsm_state_decl(bfa_ioc, uninit, struct bfa_ioc_s, enum ioc_event);
@@ -127,7 +124,6 @@ bfa_fsm_state_decl(bfa_ioc, fail, struct bfa_ioc_s, enum ioc_event);
bfa_fsm_state_decl(bfa_ioc, disabling, struct bfa_ioc_s, enum ioc_event);
bfa_fsm_state_decl(bfa_ioc, disabled, struct bfa_ioc_s, enum ioc_event);
bfa_fsm_state_decl(bfa_ioc, hwfail, struct bfa_ioc_s, enum ioc_event);
-bfa_fsm_state_decl(bfa_ioc, acq_addr, struct bfa_ioc_s, enum ioc_event);
static struct bfa_sm_table_s ioc_sm_table[] = {
{BFA_SM(bfa_ioc_sm_uninit), BFA_IOC_UNINIT},
@@ -140,7 +136,6 @@ static struct bfa_sm_table_s ioc_sm_table[] = {
{BFA_SM(bfa_ioc_sm_disabling), BFA_IOC_DISABLING},
{BFA_SM(bfa_ioc_sm_disabled), BFA_IOC_DISABLED},
{BFA_SM(bfa_ioc_sm_hwfail), BFA_IOC_HWFAIL},
- {BFA_SM(bfa_ioc_sm_acq_addr), BFA_IOC_ACQ_ADDR},
};
/*
@@ -371,17 +366,9 @@ bfa_ioc_sm_getattr(struct bfa_ioc_s *ioc, enum ioc_event event)
switch (event) {
case IOC_E_FWRSP_GETATTR:
bfa_ioc_timer_stop(ioc);
- bfa_ioc_check_attr_wwns(ioc);
- bfa_ioc_hb_monitor(ioc);
bfa_fsm_set_state(ioc, bfa_ioc_sm_op);
break;
- case IOC_E_FWRSP_ACQ_ADDR:
- bfa_ioc_timer_stop(ioc);
- bfa_ioc_hb_monitor(ioc);
- bfa_fsm_set_state(ioc, bfa_ioc_sm_acq_addr);
- break;
-
case IOC_E_PFFAILED:
case IOC_E_HWERROR:
bfa_ioc_timer_stop(ioc);
@@ -406,51 +393,6 @@ bfa_ioc_sm_getattr(struct bfa_ioc_s *ioc, enum ioc_event event)
}
}
-/*
- * Acquiring address from fabric (entry function)
- */
-static void
-bfa_ioc_sm_acq_addr_entry(struct bfa_ioc_s *ioc)
-{
-}
-
-/*
- * Acquiring address from the fabric
- */
-static void
-bfa_ioc_sm_acq_addr(struct bfa_ioc_s *ioc, enum ioc_event event)
-{
- bfa_trc(ioc, event);
-
- switch (event) {
- case IOC_E_FWRSP_GETATTR:
- bfa_ioc_check_attr_wwns(ioc);
- bfa_fsm_set_state(ioc, bfa_ioc_sm_op);
- break;
-
- case IOC_E_PFFAILED:
- case IOC_E_HWERROR:
- bfa_hb_timer_stop(ioc);
- case IOC_E_HBFAIL:
- ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE);
- bfa_fsm_set_state(ioc, bfa_ioc_sm_fail);
- if (event != IOC_E_PFFAILED)
- bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_GETATTRFAIL);
- break;
-
- case IOC_E_DISABLE:
- bfa_hb_timer_stop(ioc);
- bfa_fsm_set_state(ioc, bfa_ioc_sm_disabling);
- break;
-
- case IOC_E_ENABLE:
- break;
-
- default:
- bfa_sm_fault(ioc, event);
- }
-}
-
static void
bfa_ioc_sm_op_entry(struct bfa_ioc_s *ioc)
{
@@ -458,6 +400,7 @@ bfa_ioc_sm_op_entry(struct bfa_ioc_s *ioc)
ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_OK);
bfa_ioc_event_notify(ioc, BFA_IOC_E_ENABLED);
+ bfa_ioc_hb_monitor(ioc);
BFA_LOG(KERN_INFO, bfad, bfa_log_level, "IOC enabled\n");
bfa_ioc_aen_post(ioc, BFA_IOC_AEN_ENABLE);
}
@@ -738,26 +681,60 @@ static void
bfa_iocpf_sm_fwcheck_entry(struct bfa_iocpf_s *iocpf)
{
struct bfi_ioc_image_hdr_s fwhdr;
- u32 fwstate = readl(iocpf->ioc->ioc_regs.ioc_fwstate);
+ u32 r32, fwstate, pgnum, pgoff, loff = 0;
+ int i;
+
+ /*
+ * Spin on init semaphore to serialize.
+ */
+ r32 = readl(iocpf->ioc->ioc_regs.ioc_init_sem_reg);
+ while (r32 & 0x1) {
+ udelay(20);
+ r32 = readl(iocpf->ioc->ioc_regs.ioc_init_sem_reg);
+ }
/* h/w sem init */
- if (fwstate == BFI_IOC_UNINIT)
+ fwstate = readl(iocpf->ioc->ioc_regs.ioc_fwstate);
+ if (fwstate == BFI_IOC_UNINIT) {
+ writel(1, iocpf->ioc->ioc_regs.ioc_init_sem_reg);
goto sem_get;
+ }
bfa_ioc_fwver_get(iocpf->ioc, &fwhdr);
- if (swab32(fwhdr.exec) == BFI_FWBOOT_TYPE_NORMAL)
+ if (swab32(fwhdr.exec) == BFI_FWBOOT_TYPE_NORMAL) {
+ writel(1, iocpf->ioc->ioc_regs.ioc_init_sem_reg);
goto sem_get;
+ }
+
+ /*
+ * Clear fwver hdr
+ */
+ pgnum = PSS_SMEM_PGNUM(iocpf->ioc->ioc_regs.smem_pg0, loff);
+ pgoff = PSS_SMEM_PGOFF(loff);
+ writel(pgnum, iocpf->ioc->ioc_regs.host_page_num_fn);
+
+ for (i = 0; i < sizeof(struct bfi_ioc_image_hdr_s) / sizeof(u32); i++) {
+ bfa_mem_write(iocpf->ioc->ioc_regs.smem_page_start, loff, 0);
+ loff += sizeof(u32);
+ }
bfa_trc(iocpf->ioc, fwstate);
- bfa_trc(iocpf->ioc, fwhdr.exec);
+ bfa_trc(iocpf->ioc, swab32(fwhdr.exec));
writel(BFI_IOC_UNINIT, iocpf->ioc->ioc_regs.ioc_fwstate);
+ writel(BFI_IOC_UNINIT, iocpf->ioc->ioc_regs.alt_ioc_fwstate);
/*
- * Try to lock and then unlock the semaphore.
+ * Unlock the hw semaphore. Should be here only once per boot.
*/
readl(iocpf->ioc->ioc_regs.ioc_sem_reg);
writel(1, iocpf->ioc->ioc_regs.ioc_sem_reg);
+
+ /*
+ * unlock init semaphore.
+ */
+ writel(1, iocpf->ioc->ioc_regs.ioc_init_sem_reg);
+
sem_get:
bfa_ioc_hw_sem_get(iocpf->ioc);
}
@@ -1707,11 +1684,6 @@ bfa_ioc_download_fw(struct bfa_ioc_s *ioc, u32 boot_type,
u32 i;
u32 asicmode;
- /*
- * Initialize LMEM first before code download
- */
- bfa_ioc_lmem_init(ioc);
-
bfa_trc(ioc, bfa_cb_image_get_size(bfa_ioc_asic_gen(ioc)));
fwimg = bfa_cb_image_get_chunk(bfa_ioc_asic_gen(ioc), chunkno);
@@ -1999,6 +1971,12 @@ bfa_ioc_pll_init(struct bfa_ioc_s *ioc)
bfa_ioc_pll_init_asic(ioc);
ioc->pllinit = BFA_TRUE;
+
+ /*
+ * Initialize LMEM
+ */
+ bfa_ioc_lmem_init(ioc);
+
/*
* release semaphore.
*/
@@ -2122,10 +2100,6 @@ bfa_ioc_isr(struct bfa_ioc_s *ioc, struct bfi_mbmsg_s *m)
bfa_ioc_getattr_reply(ioc);
break;
- case BFI_IOC_I2H_ACQ_ADDR_REPLY:
- bfa_fsm_send_event(ioc, IOC_E_FWRSP_ACQ_ADDR);
- break;
-
default:
bfa_trc(ioc, msg->mh.msg_id);
WARN_ON(1);
@@ -2416,15 +2390,6 @@ bfa_ioc_is_disabled(struct bfa_ioc_s *ioc)
}
/*
- * Return TRUE if IOC is in acquiring address state
- */
-bfa_boolean_t
-bfa_ioc_is_acq_addr(struct bfa_ioc_s *ioc)
-{
- return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_acq_addr);
-}
-
-/*
* return true if IOC firmware is different.
*/
bfa_boolean_t
@@ -2916,17 +2881,6 @@ bfa_ioc_recover(struct bfa_ioc_s *ioc)
bfa_fsm_send_event(ioc, IOC_E_HBFAIL);
}
-static void
-bfa_ioc_check_attr_wwns(struct bfa_ioc_s *ioc)
-{
- if (bfa_ioc_get_type(ioc) == BFA_IOC_TYPE_LL)
- return;
- if (ioc->attr->nwwn == 0)
- bfa_ioc_aen_post(ioc, BFA_IOC_AEN_INVALID_NWWN);
- if (ioc->attr->pwwn == 0)
- bfa_ioc_aen_post(ioc, BFA_IOC_AEN_INVALID_PWWN);
-}
-
/*
* BFA IOC PF private functions
*/
@@ -4495,7 +4449,7 @@ bfa_flash_read_part(struct bfa_flash_s *flash, enum bfa_flash_part_type type,
*/
#define BFA_DIAG_MEMTEST_TOV 50000 /* memtest timeout in msec */
-#define BFA_DIAG_FWPING_TOV 1000 /* msec */
+#define CT2_BFA_DIAG_MEMTEST_TOV (9*30*1000) /* 4.5 min */
/* IOC event handler */
static void
@@ -4772,7 +4726,7 @@ diag_ledtest_send(struct bfa_diag_s *diag, struct bfa_diag_ledtest_s *ledtest)
}
static void
-diag_ledtest_comp(struct bfa_diag_s *diag, struct bfi_diag_ledtest_rsp_s * msg)
+diag_ledtest_comp(struct bfa_diag_s *diag, struct bfi_diag_ledtest_rsp_s *msg)
{
bfa_trc(diag, diag->ledtest.lock);
diag->ledtest.lock = BFA_FALSE;
@@ -4850,6 +4804,8 @@ bfa_diag_memtest(struct bfa_diag_s *diag, struct bfa_diag_memtest_s *memtest,
u32 pattern, struct bfa_diag_memtest_result *result,
bfa_cb_diag_t cbfn, void *cbarg)
{
+ u32 memtest_tov;
+
bfa_trc(diag, pattern);
if (!bfa_ioc_adapter_is_disabled(diag->ioc))
@@ -4869,8 +4825,10 @@ bfa_diag_memtest(struct bfa_diag_s *diag, struct bfa_diag_memtest_s *memtest,
/* download memtest code and take LPU0 out of reset */
bfa_ioc_boot(diag->ioc, BFI_FWBOOT_TYPE_MEMTEST, BFI_FWBOOT_ENV_OS);
+ memtest_tov = (bfa_ioc_asic_gen(diag->ioc) == BFI_ASIC_GEN_CT2) ?
+ CT2_BFA_DIAG_MEMTEST_TOV : BFA_DIAG_MEMTEST_TOV;
bfa_timer_begin(diag->ioc->timer_mod, &diag->timer,
- bfa_diag_memtest_done, diag, BFA_DIAG_MEMTEST_TOV);
+ bfa_diag_memtest_done, diag, memtest_tov);
diag->timer_active = 1;
return BFA_STATUS_OK;
}
@@ -5641,24 +5599,27 @@ bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
case BFA_DCONF_SM_INIT:
if (dconf->min_cfg) {
bfa_trc(dconf->bfa, dconf->min_cfg);
+ bfa_fsm_send_event(&dconf->bfa->iocfc,
+ IOCFC_E_DCONF_DONE);
return;
}
bfa_sm_set_state(dconf, bfa_dconf_sm_flash_read);
- dconf->flashdone = BFA_FALSE;
- bfa_trc(dconf->bfa, dconf->flashdone);
+ bfa_timer_start(dconf->bfa, &dconf->timer,
+ bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV);
bfa_status = bfa_flash_read_part(BFA_FLASH(dconf->bfa),
BFA_FLASH_PART_DRV, dconf->instance,
dconf->dconf,
sizeof(struct bfa_dconf_s), 0,
bfa_dconf_init_cb, dconf->bfa);
if (bfa_status != BFA_STATUS_OK) {
+ bfa_timer_stop(&dconf->timer);
bfa_dconf_init_cb(dconf->bfa, BFA_STATUS_FAILED);
bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
return;
}
break;
case BFA_DCONF_SM_EXIT:
- dconf->flashdone = BFA_TRUE;
+ bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
case BFA_DCONF_SM_IOCDISABLE:
case BFA_DCONF_SM_WR:
case BFA_DCONF_SM_FLASH_COMP:
@@ -5679,15 +5640,20 @@ bfa_dconf_sm_flash_read(struct bfa_dconf_mod_s *dconf,
switch (event) {
case BFA_DCONF_SM_FLASH_COMP:
+ bfa_timer_stop(&dconf->timer);
bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
break;
case BFA_DCONF_SM_TIMEOUT:
bfa_sm_set_state(dconf, bfa_dconf_sm_ready);
+ bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_IOC_FAILED);
break;
case BFA_DCONF_SM_EXIT:
- dconf->flashdone = BFA_TRUE;
- bfa_trc(dconf->bfa, dconf->flashdone);
+ bfa_timer_stop(&dconf->timer);
+ bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
+ break;
case BFA_DCONF_SM_IOCDISABLE:
+ bfa_timer_stop(&dconf->timer);
bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
break;
default:
@@ -5710,9 +5676,8 @@ bfa_dconf_sm_ready(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
break;
case BFA_DCONF_SM_EXIT:
- dconf->flashdone = BFA_TRUE;
- bfa_trc(dconf->bfa, dconf->flashdone);
bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
break;
case BFA_DCONF_SM_INIT:
case BFA_DCONF_SM_IOCDISABLE:
@@ -5774,9 +5739,7 @@ bfa_dconf_sm_final_sync(struct bfa_dconf_mod_s *dconf,
bfa_timer_stop(&dconf->timer);
case BFA_DCONF_SM_TIMEOUT:
bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
- dconf->flashdone = BFA_TRUE;
- bfa_trc(dconf->bfa, dconf->flashdone);
- bfa_ioc_disable(&dconf->bfa->ioc);
+ bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
break;
default:
bfa_sm_fault(dconf->bfa, event);
@@ -5823,8 +5786,8 @@ bfa_dconf_sm_iocdown_dirty(struct bfa_dconf_mod_s *dconf,
bfa_sm_set_state(dconf, bfa_dconf_sm_dirty);
break;
case BFA_DCONF_SM_EXIT:
- dconf->flashdone = BFA_TRUE;
bfa_sm_set_state(dconf, bfa_dconf_sm_uninit);
+ bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_DCONF_DONE);
break;
case BFA_DCONF_SM_IOCDISABLE:
break;
@@ -5865,11 +5828,6 @@ bfa_dconf_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg,
if (cfg->drvcfg.min_cfg) {
bfa_mem_kva_curp(dconf) += sizeof(struct bfa_dconf_hdr_s);
dconf->min_cfg = BFA_TRUE;
- /*
- * Set the flashdone flag to TRUE explicitly as no flash
- * write will happen in min_cfg mode.
- */
- dconf->flashdone = BFA_TRUE;
} else {
dconf->min_cfg = BFA_FALSE;
bfa_mem_kva_curp(dconf) += sizeof(struct bfa_dconf_s);
@@ -5885,9 +5843,7 @@ bfa_dconf_init_cb(void *arg, bfa_status_t status)
struct bfa_s *bfa = arg;
struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
- dconf->flashdone = BFA_TRUE;
- bfa_trc(bfa, dconf->flashdone);
- bfa_iocfc_cb_dconf_modinit(bfa, status);
+ bfa_sm_send_event(dconf, BFA_DCONF_SM_FLASH_COMP);
if (status == BFA_STATUS_OK) {
bfa_dconf_read_data_valid(bfa) = BFA_TRUE;
if (dconf->dconf->hdr.signature != BFI_DCONF_SIGNATURE)
@@ -5895,7 +5851,7 @@ bfa_dconf_init_cb(void *arg, bfa_status_t status)
if (dconf->dconf->hdr.version != BFI_DCONF_VERSION)
dconf->dconf->hdr.version = BFI_DCONF_VERSION;
}
- bfa_sm_send_event(dconf, BFA_DCONF_SM_FLASH_COMP);
+ bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_DCONF_DONE);
}
void
@@ -5977,7 +5933,5 @@ void
bfa_dconf_modexit(struct bfa_s *bfa)
{
struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa);
- BFA_DCONF_MOD(bfa)->flashdone = BFA_FALSE;
- bfa_trc(bfa, BFA_DCONF_MOD(bfa)->flashdone);
bfa_sm_send_event(dconf, BFA_DCONF_SM_EXIT);
}
diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h
index 546d46b..1a99d4b 100644
--- a/drivers/scsi/bfa/bfa_ioc.h
+++ b/drivers/scsi/bfa/bfa_ioc.h
@@ -373,6 +373,22 @@ struct bfa_cb_qe_s {
};
/*
+ * IOCFC state machine definitions/declarations
+ */
+enum iocfc_event {
+ IOCFC_E_INIT = 1, /* IOCFC init request */
+ IOCFC_E_START = 2, /* IOCFC mod start request */
+ IOCFC_E_STOP = 3, /* IOCFC stop request */
+ IOCFC_E_ENABLE = 4, /* IOCFC enable request */
+ IOCFC_E_DISABLE = 5, /* IOCFC disable request */
+ IOCFC_E_IOC_ENABLED = 6, /* IOC enabled message */
+ IOCFC_E_IOC_DISABLED = 7, /* IOC disabled message */
+ IOCFC_E_IOC_FAILED = 8, /* failure notice by IOC sm */
+ IOCFC_E_DCONF_DONE = 9, /* dconf read/write done */
+ IOCFC_E_CFG_DONE = 10, /* IOCFC config complete */
+};
+
+/*
* ASIC block configurtion related
*/
@@ -706,7 +722,6 @@ struct bfa_dconf_s {
struct bfa_dconf_mod_s {
bfa_sm_t sm;
u8 instance;
- bfa_boolean_t flashdone;
bfa_boolean_t read_data_valid;
bfa_boolean_t min_cfg;
struct bfa_timer_s timer;
diff --git a/drivers/scsi/bfa/bfa_ioc_ct.c b/drivers/scsi/bfa/bfa_ioc_ct.c
index d1b8f0c..2eb0c6a 100644
--- a/drivers/scsi/bfa/bfa_ioc_ct.c
+++ b/drivers/scsi/bfa/bfa_ioc_ct.c
@@ -786,17 +786,73 @@ bfa_ioc_ct2_mac_reset(void __iomem *rb)
}
#define CT2_NFC_MAX_DELAY 1000
+#define CT2_NFC_VER_VALID 0x143
+#define BFA_IOC_PLL_POLL 1000000
+
+static bfa_boolean_t
+bfa_ioc_ct2_nfc_halted(void __iomem *rb)
+{
+ u32 r32;
+
+ r32 = readl(rb + CT2_NFC_CSR_SET_REG);
+ if (r32 & __NFC_CONTROLLER_HALTED)
+ return BFA_TRUE;
+
+ return BFA_FALSE;
+}
+
+static void
+bfa_ioc_ct2_nfc_resume(void __iomem *rb)
+{
+ u32 r32;
+ int i;
+
+ writel(__HALT_NFC_CONTROLLER, rb + CT2_NFC_CSR_CLR_REG);
+ for (i = 0; i < CT2_NFC_MAX_DELAY; i++) {
+ r32 = readl(rb + CT2_NFC_CSR_SET_REG);
+ if (!(r32 & __NFC_CONTROLLER_HALTED))
+ return;
+ udelay(1000);
+ }
+ WARN_ON(1);
+}
+
bfa_status_t
bfa_ioc_ct2_pll_init(void __iomem *rb, enum bfi_asic_mode mode)
{
- u32 wgn, r32;
- int i;
+ u32 wgn, r32, nfc_ver, i;
- /*
- * Initialize PLL if not already done by NFC
- */
wgn = readl(rb + CT2_WGN_STATUS);
- if (!(wgn & __GLBL_PF_VF_CFG_RDY)) {
+ nfc_ver = readl(rb + CT2_RSC_GPR15_REG);
+
+ if ((wgn == (__A2T_AHB_LOAD | __WGN_READY)) &&
+ (nfc_ver >= CT2_NFC_VER_VALID)) {
+ if (bfa_ioc_ct2_nfc_halted(rb))
+ bfa_ioc_ct2_nfc_resume(rb);
+
+ writel(__RESET_AND_START_SCLK_LCLK_PLLS,
+ rb + CT2_CSI_FW_CTL_SET_REG);
+
+ for (i = 0; i < BFA_IOC_PLL_POLL; i++) {
+ r32 = readl(rb + CT2_APP_PLL_LCLK_CTL_REG);
+ if (r32 & __RESET_AND_START_SCLK_LCLK_PLLS)
+ break;
+ }
+
+ WARN_ON(!(r32 & __RESET_AND_START_SCLK_LCLK_PLLS));
+
+ for (i = 0; i < BFA_IOC_PLL_POLL; i++) {
+ r32 = readl(rb + CT2_APP_PLL_LCLK_CTL_REG);
+ if (!(r32 & __RESET_AND_START_SCLK_LCLK_PLLS))
+ break;
+ }
+
+ WARN_ON(r32 & __RESET_AND_START_SCLK_LCLK_PLLS);
+ udelay(1000);
+
+ r32 = readl(rb + CT2_CSI_FW_CTL_REG);
+ WARN_ON(r32 & __RESET_AND_START_SCLK_LCLK_PLLS);
+ } else {
writel(__HALT_NFC_CONTROLLER, rb + CT2_NFC_CSR_SET_REG);
for (i = 0; i < CT2_NFC_MAX_DELAY; i++) {
r32 = readl(rb + CT2_NFC_CSR_SET_REG);
@@ -804,57 +860,62 @@ bfa_ioc_ct2_pll_init(void __iomem *rb, enum bfi_asic_mode mode)
break;
udelay(1000);
}
- }
- /*
- * Mask the interrupts and clear any
- * pending interrupts.
- */
- writel(1, (rb + CT2_LPU0_HOSTFN_MBOX0_MSK));
- writel(1, (rb + CT2_LPU1_HOSTFN_MBOX0_MSK));
-
- r32 = readl((rb + CT2_LPU0_HOSTFN_CMD_STAT));
- if (r32 == 1) {
- writel(1, (rb + CT2_LPU0_HOSTFN_CMD_STAT));
- readl((rb + CT2_LPU0_HOSTFN_CMD_STAT));
+ bfa_ioc_ct2_mac_reset(rb);
+ bfa_ioc_ct2_sclk_init(rb);
+ bfa_ioc_ct2_lclk_init(rb);
+
+ /*
+ * release soft reset on s_clk & l_clk
+ */
+ r32 = readl(rb + CT2_APP_PLL_SCLK_CTL_REG);
+ writel(r32 & ~__APP_PLL_SCLK_LOGIC_SOFT_RESET,
+ (rb + CT2_APP_PLL_SCLK_CTL_REG));
+
+ /*
+ * release soft reset on s_clk & l_clk
+ */
+ r32 = readl(rb + CT2_APP_PLL_LCLK_CTL_REG);
+ writel(r32 & ~__APP_PLL_LCLK_LOGIC_SOFT_RESET,
+ (rb + CT2_APP_PLL_LCLK_CTL_REG));
}
- r32 = readl((rb + CT2_LPU1_HOSTFN_CMD_STAT));
- if (r32 == 1) {
- writel(1, (rb + CT2_LPU1_HOSTFN_CMD_STAT));
- readl((rb + CT2_LPU1_HOSTFN_CMD_STAT));
- }
-
- bfa_ioc_ct2_mac_reset(rb);
- bfa_ioc_ct2_sclk_init(rb);
- bfa_ioc_ct2_lclk_init(rb);
-
- /*
- * release soft reset on s_clk & l_clk
- */
- r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG));
- writel(r32 & ~__APP_PLL_SCLK_LOGIC_SOFT_RESET,
- (rb + CT2_APP_PLL_SCLK_CTL_REG));
-
- /*
- * release soft reset on s_clk & l_clk
- */
- r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG));
- writel(r32 & ~__APP_PLL_LCLK_LOGIC_SOFT_RESET,
- (rb + CT2_APP_PLL_LCLK_CTL_REG));
/*
* Announce flash device presence, if flash was corrupted.
*/
if (wgn == (__WGN_READY | __GLBL_PF_VF_CFG_RDY)) {
- r32 = readl((rb + PSS_GPIO_OUT_REG));
+ r32 = readl(rb + PSS_GPIO_OUT_REG);
writel(r32 & ~1, (rb + PSS_GPIO_OUT_REG));
- r32 = readl((rb + PSS_GPIO_OE_REG));
+ r32 = readl(rb + PSS_GPIO_OE_REG);
writel(r32 | 1, (rb + PSS_GPIO_OE_REG));
}
+ /*
+ * Mask the interrupts and clear any
+ * pending interrupts.
+ */
+ writel(1, (rb + CT2_LPU0_HOSTFN_MBOX0_MSK));
+ writel(1, (rb + CT2_LPU1_HOSTFN_MBOX0_MSK));
+
+ /* For first time initialization, no need to clear interrupts */
+ r32 = readl(rb + HOST_SEM5_REG);
+ if (r32 & 0x1) {
+ r32 = readl(rb + CT2_LPU0_HOSTFN_CMD_STAT);
+ if (r32 == 1) {
+ writel(1, rb + CT2_LPU0_HOSTFN_CMD_STAT);
+ readl((rb + CT2_LPU0_HOSTFN_CMD_STAT));
+ }
+ r32 = readl(rb + CT2_LPU1_HOSTFN_CMD_STAT);
+ if (r32 == 1) {
+ writel(1, rb + CT2_LPU1_HOSTFN_CMD_STAT);
+ readl(rb + CT2_LPU1_HOSTFN_CMD_STAT);
+ }
+ }
+
bfa_ioc_ct2_mem_init(rb);
- writel(BFI_IOC_UNINIT, (rb + CT2_BFA_IOC0_STATE_REG));
- writel(BFI_IOC_UNINIT, (rb + CT2_BFA_IOC1_STATE_REG));
+ writel(BFI_IOC_UNINIT, rb + CT2_BFA_IOC0_STATE_REG);
+ writel(BFI_IOC_UNINIT, rb + CT2_BFA_IOC1_STATE_REG);
+
return BFA_STATUS_OK;
}
diff --git a/drivers/scsi/bfa/bfa_svc.c b/drivers/scsi/bfa/bfa_svc.c
index aa8a0ea..2e856e6 100644
--- a/drivers/scsi/bfa/bfa_svc.c
+++ b/drivers/scsi/bfa/bfa_svc.c
@@ -1280,6 +1280,7 @@ bfa_lps_sm_loginwait(struct bfa_lps_s *lps, enum bfa_lps_event event)
switch (event) {
case BFA_LPS_SM_RESUME:
bfa_sm_set_state(lps, bfa_lps_sm_login);
+ bfa_lps_send_login(lps);
break;
case BFA_LPS_SM_OFFLINE:
@@ -1578,7 +1579,7 @@ bfa_lps_login_rsp(struct bfa_s *bfa, struct bfi_lps_login_rsp_s *rsp)
break;
case BFA_STATUS_VPORT_MAX:
- if (!rsp->ext_status)
+ if (rsp->ext_status)
bfa_lps_no_res(lps, rsp->ext_status);
break;
@@ -3084,33 +3085,6 @@ bfa_fcport_set_wwns(struct bfa_fcport_s *fcport)
}
static void
-bfa_fcport_send_txcredit(void *port_cbarg)
-{
-
- struct bfa_fcport_s *fcport = port_cbarg;
- struct bfi_fcport_set_svc_params_req_s *m;
-
- /*
- * check for room in queue to send request now
- */
- m = bfa_reqq_next(fcport->bfa, BFA_REQQ_PORT);
- if (!m) {
- bfa_trc(fcport->bfa, fcport->cfg.tx_bbcredit);
- return;
- }
-
- bfi_h2i_set(m->mh, BFI_MC_FCPORT, BFI_FCPORT_H2I_SET_SVC_PARAMS_REQ,
- bfa_fn_lpu(fcport->bfa));
- m->tx_bbcredit = cpu_to_be16((u16)fcport->cfg.tx_bbcredit);
- m->bb_scn = fcport->cfg.bb_scn;
-
- /*
- * queue I/O message to firmware
- */
- bfa_reqq_produce(fcport->bfa, BFA_REQQ_PORT, m->mh);
-}
-
-static void
bfa_fcport_qos_stats_swap(struct bfa_qos_stats_s *d,
struct bfa_qos_stats_s *s)
{
@@ -3602,26 +3576,24 @@ bfa_fcport_cfg_speed(struct bfa_s *bfa, enum bfa_port_speed speed)
return BFA_STATUS_UNSUPP_SPEED;
}
- /* For Mezz card, port speed entered needs to be checked */
- if (bfa_mfg_is_mezz(fcport->bfa->ioc.attr->card_type)) {
- if (bfa_ioc_get_type(&fcport->bfa->ioc) == BFA_IOC_TYPE_FC) {
- /* For CT2, 1G is not supported */
- if ((speed == BFA_PORT_SPEED_1GBPS) &&
- (bfa_asic_id_ct2(bfa->ioc.pcidev.device_id)))
- return BFA_STATUS_UNSUPP_SPEED;
+ /* Port speed entered needs to be checked */
+ if (bfa_ioc_get_type(&fcport->bfa->ioc) == BFA_IOC_TYPE_FC) {
+ /* For CT2, 1G is not supported */
+ if ((speed == BFA_PORT_SPEED_1GBPS) &&
+ (bfa_asic_id_ct2(bfa->ioc.pcidev.device_id)))
+ return BFA_STATUS_UNSUPP_SPEED;
- /* Already checked for Auto Speed and Max Speed supp */
- if (!(speed == BFA_PORT_SPEED_1GBPS ||
- speed == BFA_PORT_SPEED_2GBPS ||
- speed == BFA_PORT_SPEED_4GBPS ||
- speed == BFA_PORT_SPEED_8GBPS ||
- speed == BFA_PORT_SPEED_16GBPS ||
- speed == BFA_PORT_SPEED_AUTO))
- return BFA_STATUS_UNSUPP_SPEED;
- } else {
- if (speed != BFA_PORT_SPEED_10GBPS)
- return BFA_STATUS_UNSUPP_SPEED;
- }
+ /* Already checked for Auto Speed and Max Speed supp */
+ if (!(speed == BFA_PORT_SPEED_1GBPS ||
+ speed == BFA_PORT_SPEED_2GBPS ||
+ speed == BFA_PORT_SPEED_4GBPS ||
+ speed == BFA_PORT_SPEED_8GBPS ||
+ speed == BFA_PORT_SPEED_16GBPS ||
+ speed == BFA_PORT_SPEED_AUTO))
+ return BFA_STATUS_UNSUPP_SPEED;
+ } else {
+ if (speed != BFA_PORT_SPEED_10GBPS)
+ return BFA_STATUS_UNSUPP_SPEED;
}
fcport->cfg.speed = speed;
@@ -3765,7 +3737,6 @@ bfa_fcport_set_tx_bbcredit(struct bfa_s *bfa, u16 tx_bbcredit, u8 bb_scn)
fcport->cfg.bb_scn = bb_scn;
if (bb_scn)
fcport->bbsc_op_state = BFA_TRUE;
- bfa_fcport_send_txcredit(fcport);
}
/*
@@ -3825,8 +3796,6 @@ bfa_fcport_get_attr(struct bfa_s *bfa, struct bfa_port_attr_s *attr)
attr->port_state = BFA_PORT_ST_IOCDIS;
else if (bfa_ioc_fw_mismatch(&fcport->bfa->ioc))
attr->port_state = BFA_PORT_ST_FWMISMATCH;
- else if (bfa_ioc_is_acq_addr(&fcport->bfa->ioc))
- attr->port_state = BFA_PORT_ST_ACQ_ADDR;
}
/* FCoE vlan */
diff --git a/drivers/scsi/bfa/bfa_svc.h b/drivers/scsi/bfa/bfa_svc.h
index b52cbb6..f300675 100644
--- a/drivers/scsi/bfa/bfa_svc.h
+++ b/drivers/scsi/bfa/bfa_svc.h
@@ -663,10 +663,6 @@ void bfa_cb_lps_fdisclogo_comp(void *bfad, void *uarg);
void bfa_cb_lps_cvl_event(void *bfad, void *uarg);
/* FAA specific APIs */
-bfa_status_t bfa_faa_enable(struct bfa_s *bfa,
- bfa_cb_iocfc_t cbfn, void *cbarg);
-bfa_status_t bfa_faa_disable(struct bfa_s *bfa,
- bfa_cb_iocfc_t cbfn, void *cbarg);
bfa_status_t bfa_faa_query(struct bfa_s *bfa, struct bfa_faa_attr_s *attr,
bfa_cb_iocfc_t cbfn, void *cbarg);
diff --git a/drivers/scsi/bfa/bfad_attr.c b/drivers/scsi/bfa/bfad_attr.c
index 1938fe0..7b1ecd2 100644
--- a/drivers/scsi/bfa/bfad_attr.c
+++ b/drivers/scsi/bfa/bfad_attr.c
@@ -442,6 +442,43 @@ bfad_im_vport_create(struct fc_vport *fc_vport, bool disable)
return status;
}
+int
+bfad_im_issue_fc_host_lip(struct Scsi_Host *shost)
+{
+ struct bfad_im_port_s *im_port =
+ (struct bfad_im_port_s *) shost->hostdata[0];
+ struct bfad_s *bfad = im_port->bfad;
+ struct bfad_hal_comp fcomp;
+ unsigned long flags;
+ uint32_t status;
+
+ init_completion(&fcomp.comp);
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ status = bfa_port_disable(&bfad->bfa.modules.port,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+
+ if (status != BFA_STATUS_OK)
+ return -EIO;
+
+ wait_for_completion(&fcomp.comp);
+ if (fcomp.status != BFA_STATUS_OK)
+ return -EIO;
+
+ spin_lock_irqsave(&bfad->bfad_lock, flags);
+ status = bfa_port_enable(&bfad->bfa.modules.port,
+ bfad_hcb_comp, &fcomp);
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ if (status != BFA_STATUS_OK)
+ return -EIO;
+
+ wait_for_completion(&fcomp.comp);
+ if (fcomp.status != BFA_STATUS_OK)
+ return -EIO;
+
+ return 0;
+}
+
static int
bfad_im_vport_delete(struct fc_vport *fc_vport)
{
@@ -457,8 +494,11 @@ bfad_im_vport_delete(struct fc_vport *fc_vport)
unsigned long flags;
struct completion fcomp;
- if (im_port->flags & BFAD_PORT_DELETE)
- goto free_scsi_host;
+ if (im_port->flags & BFAD_PORT_DELETE) {
+ bfad_scsi_host_free(bfad, im_port);
+ list_del(&vport->list_entry);
+ return 0;
+ }
port = im_port->port;
@@ -489,7 +529,6 @@ bfad_im_vport_delete(struct fc_vport *fc_vport)
wait_for_completion(vport->comp_del);
-free_scsi_host:
bfad_scsi_host_free(bfad, im_port);
list_del(&vport->list_entry);
kfree(vport);
@@ -579,7 +618,7 @@ struct fc_function_template bfad_im_fc_function_template = {
.show_rport_dev_loss_tmo = 1,
.get_rport_dev_loss_tmo = bfad_im_get_rport_loss_tmo,
.set_rport_dev_loss_tmo = bfad_im_set_rport_loss_tmo,
-
+ .issue_fc_host_lip = bfad_im_issue_fc_host_lip,
.vport_create = bfad_im_vport_create,
.vport_delete = bfad_im_vport_delete,
.vport_disable = bfad_im_vport_disable,
diff --git a/drivers/scsi/bfa/bfad_bsg.c b/drivers/scsi/bfa/bfad_bsg.c
index 8005c6c..e1f4b10 100644
--- a/drivers/scsi/bfa/bfad_bsg.c
+++ b/drivers/scsi/bfa/bfad_bsg.c
@@ -1288,50 +1288,6 @@ out:
}
int
-bfad_iocmd_faa_enable(struct bfad_s *bfad, void *cmd)
-{
- struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
- unsigned long flags;
- struct bfad_hal_comp fcomp;
-
- init_completion(&fcomp.comp);
- iocmd->status = BFA_STATUS_OK;
- spin_lock_irqsave(&bfad->bfad_lock, flags);
- iocmd->status = bfa_faa_enable(&bfad->bfa, bfad_hcb_comp, &fcomp);
- spin_unlock_irqrestore(&bfad->bfad_lock, flags);
-
- if (iocmd->status != BFA_STATUS_OK)
- goto out;
-
- wait_for_completion(&fcomp.comp);
- iocmd->status = fcomp.status;
-out:
- return 0;
-}
-
-int
-bfad_iocmd_faa_disable(struct bfad_s *bfad, void *cmd)
-{
- struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)cmd;
- unsigned long flags;
- struct bfad_hal_comp fcomp;
-
- init_completion(&fcomp.comp);
- iocmd->status = BFA_STATUS_OK;
- spin_lock_irqsave(&bfad->bfad_lock, flags);
- iocmd->status = bfa_faa_disable(&bfad->bfa, bfad_hcb_comp, &fcomp);
- spin_unlock_irqrestore(&bfad->bfad_lock, flags);
-
- if (iocmd->status != BFA_STATUS_OK)
- goto out;
-
- wait_for_completion(&fcomp.comp);
- iocmd->status = fcomp.status;
-out:
- return 0;
-}
-
-int
bfad_iocmd_faa_query(struct bfad_s *bfad, void *cmd)
{
struct bfa_bsg_faa_attr_s *iocmd = (struct bfa_bsg_faa_attr_s *)cmd;
@@ -1918,6 +1874,7 @@ bfad_iocmd_debug_fw_core(struct bfad_s *bfad, void *cmd,
struct bfa_bsg_debug_s *iocmd = (struct bfa_bsg_debug_s *)cmd;
void *iocmd_bufptr;
unsigned long flags;
+ u32 offset;
if (bfad_chk_iocmd_sz(payload_len, sizeof(struct bfa_bsg_debug_s),
BFA_DEBUG_FW_CORE_CHUNK_SZ) != BFA_STATUS_OK) {
@@ -1935,8 +1892,10 @@ bfad_iocmd_debug_fw_core(struct bfad_s *bfad, void *cmd,
iocmd_bufptr = (char *)iocmd + sizeof(struct bfa_bsg_debug_s);
spin_lock_irqsave(&bfad->bfad_lock, flags);
+ offset = iocmd->offset;
iocmd->status = bfa_ioc_debug_fwcore(&bfad->bfa.ioc, iocmd_bufptr,
- (u32 *)&iocmd->offset, &iocmd->bufsz);
+ &offset, &iocmd->bufsz);
+ iocmd->offset = offset;
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
out:
return 0;
@@ -2633,12 +2592,6 @@ bfad_iocmd_handler(struct bfad_s *bfad, unsigned int cmd, void *iocmd,
case IOCMD_FLASH_DISABLE_OPTROM:
rc = bfad_iocmd_ablk_optrom(bfad, cmd, iocmd);
break;
- case IOCMD_FAA_ENABLE:
- rc = bfad_iocmd_faa_enable(bfad, iocmd);
- break;
- case IOCMD_FAA_DISABLE:
- rc = bfad_iocmd_faa_disable(bfad, iocmd);
- break;
case IOCMD_FAA_QUERY:
rc = bfad_iocmd_faa_query(bfad, iocmd);
break;
@@ -2809,9 +2762,16 @@ bfad_im_bsg_vendor_request(struct fc_bsg_job *job)
struct bfad_im_port_s *im_port =
(struct bfad_im_port_s *) job->shost->hostdata[0];
struct bfad_s *bfad = im_port->bfad;
+ struct request_queue *request_q = job->req->q;
void *payload_kbuf;
int rc = -EINVAL;
+ /*
+ * Set the BSG device request_queue size to 256 to support
+ * payloads larger than 512*1024K bytes.
+ */
+ blk_queue_max_segments(request_q, 256);
+
/* Allocate a temp buffer to hold the passed in user space command */
payload_kbuf = kzalloc(job->request_payload.payload_len, GFP_KERNEL);
if (!payload_kbuf) {
diff --git a/drivers/scsi/bfa/bfad_bsg.h b/drivers/scsi/bfa/bfad_bsg.h
index e859adb..17ad672 100644
--- a/drivers/scsi/bfa/bfad_bsg.h
+++ b/drivers/scsi/bfa/bfad_bsg.h
@@ -83,8 +83,6 @@ enum {
IOCMD_PORT_CFG_MODE,
IOCMD_FLASH_ENABLE_OPTROM,
IOCMD_FLASH_DISABLE_OPTROM,
- IOCMD_FAA_ENABLE,
- IOCMD_FAA_DISABLE,
IOCMD_FAA_QUERY,
IOCMD_CEE_GET_ATTR,
IOCMD_CEE_GET_STATS,
diff --git a/drivers/scsi/bfa/bfad_drv.h b/drivers/scsi/bfa/bfad_drv.h
index dc5b9d9..7f74f1d 100644
--- a/drivers/scsi/bfa/bfad_drv.h
+++ b/drivers/scsi/bfa/bfad_drv.h
@@ -56,7 +56,7 @@
#ifdef BFA_DRIVER_VERSION
#define BFAD_DRIVER_VERSION BFA_DRIVER_VERSION
#else
-#define BFAD_DRIVER_VERSION "3.0.2.2"
+#define BFAD_DRIVER_VERSION "3.0.23.0"
#endif
#define BFAD_PROTO_NAME FCPI_NAME
diff --git a/drivers/scsi/bfa/bfi_ms.h b/drivers/scsi/bfa/bfi_ms.h
index 0d9f1fb..d4220e1 100644
--- a/drivers/scsi/bfa/bfi_ms.h
+++ b/drivers/scsi/bfa/bfi_ms.h
@@ -28,17 +28,15 @@ enum bfi_iocfc_h2i_msgs {
BFI_IOCFC_H2I_CFG_REQ = 1,
BFI_IOCFC_H2I_SET_INTR_REQ = 2,
BFI_IOCFC_H2I_UPDATEQ_REQ = 3,
- BFI_IOCFC_H2I_FAA_ENABLE_REQ = 4,
- BFI_IOCFC_H2I_FAA_DISABLE_REQ = 5,
- BFI_IOCFC_H2I_FAA_QUERY_REQ = 6,
+ BFI_IOCFC_H2I_FAA_QUERY_REQ = 4,
+ BFI_IOCFC_H2I_ADDR_REQ = 5,
};
enum bfi_iocfc_i2h_msgs {
BFI_IOCFC_I2H_CFG_REPLY = BFA_I2HM(1),
BFI_IOCFC_I2H_UPDATEQ_RSP = BFA_I2HM(3),
- BFI_IOCFC_I2H_FAA_ENABLE_RSP = BFA_I2HM(4),
- BFI_IOCFC_I2H_FAA_DISABLE_RSP = BFA_I2HM(5),
- BFI_IOCFC_I2H_FAA_QUERY_RSP = BFA_I2HM(6),
+ BFI_IOCFC_I2H_FAA_QUERY_RSP = BFA_I2HM(4),
+ BFI_IOCFC_I2H_ADDR_MSG = BFA_I2HM(5),
};
struct bfi_iocfc_cfg_s {
@@ -184,6 +182,13 @@ struct bfi_faa_en_dis_s {
struct bfi_mhdr_s mh; /* common msg header */
};
+struct bfi_faa_addr_msg_s {
+ struct bfi_mhdr_s mh; /* common msg header */
+ u8 rsvd[4];
+ wwn_t pwwn; /* Fabric acquired PWWN */
+ wwn_t nwwn; /* Fabric acquired PWWN */
+};
+
/*
* BFI_IOCFC_H2I_FAA_QUERY_REQ message
*/
diff --git a/drivers/scsi/bfa/bfi_reg.h b/drivers/scsi/bfa/bfi_reg.h
index d892064..ed5f159 100644
--- a/drivers/scsi/bfa/bfi_reg.h
+++ b/drivers/scsi/bfa/bfi_reg.h
@@ -335,11 +335,17 @@ enum {
#define __PMM_1T_PNDB_P 0x00000002
#define CT2_PMM_1T_CONTROL_REG_P1 0x00023c1c
#define CT2_WGN_STATUS 0x00014990
+#define __A2T_AHB_LOAD 0x00000800
#define __WGN_READY 0x00000400
#define __GLBL_PF_VF_CFG_RDY 0x00000200
+#define CT2_NFC_CSR_CLR_REG 0x00027420
#define CT2_NFC_CSR_SET_REG 0x00027424
#define __HALT_NFC_CONTROLLER 0x00000002
#define __NFC_CONTROLLER_HALTED 0x00001000
+#define CT2_RSC_GPR15_REG 0x0002765c
+#define CT2_CSI_FW_CTL_REG 0x00027080
+#define CT2_CSI_FW_CTL_SET_REG 0x00027088
+#define __RESET_AND_START_SCLK_LCLK_PLLS 0x00010000
#define CT2_CSI_MAC0_CONTROL_REG 0x000270d0
#define __CSI_MAC_RESET 0x00000010
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index abd72a0..c1c6a92 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -439,13 +439,13 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
fr->fr_dev = lport;
bg = &bnx2fc_global;
- spin_lock_bh(&bg->fcoe_rx_list.lock);
+ spin_lock(&bg->fcoe_rx_list.lock);
__skb_queue_tail(&bg->fcoe_rx_list, skb);
if (bg->fcoe_rx_list.qlen == 1)
wake_up_process(bg->thread);
- spin_unlock_bh(&bg->fcoe_rx_list.lock);
+ spin_unlock(&bg->fcoe_rx_list.lock);
return 0;
err:
diff --git a/drivers/scsi/dtc.c b/drivers/scsi/dtc.c
index c2677ba..4b11bb0 100644
--- a/drivers/scsi/dtc.c
+++ b/drivers/scsi/dtc.c
@@ -72,7 +72,6 @@
#endif
-#include <asm/system.h>
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/blkdev.h>
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index ae7d15c..335e851 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1436,7 +1436,7 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
goto err;
fps = &per_cpu(fcoe_percpu, cpu);
- spin_lock_bh(&fps->fcoe_rx_list.lock);
+ spin_lock(&fps->fcoe_rx_list.lock);
if (unlikely(!fps->thread)) {
/*
* The targeted CPU is not ready, let's target
@@ -1447,12 +1447,12 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
"ready for incoming skb- using first online "
"CPU.\n");
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
+ spin_unlock(&fps->fcoe_rx_list.lock);
cpu = cpumask_first(cpu_online_mask);
fps = &per_cpu(fcoe_percpu, cpu);
- spin_lock_bh(&fps->fcoe_rx_list.lock);
+ spin_lock(&fps->fcoe_rx_list.lock);
if (!fps->thread) {
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
+ spin_unlock(&fps->fcoe_rx_list.lock);
goto err;
}
}
@@ -1463,24 +1463,17 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
* so we're free to queue skbs into it's queue.
*/
- /* If this is a SCSI-FCP frame, and this is already executing on the
- * correct CPU, and the queue for this CPU is empty, then go ahead
- * and process the frame directly in the softirq context.
- * This lets us process completions without context switching from the
- * NET_RX softirq, to our receive processing thread, and then back to
- * BLOCK softirq context.
+ /*
+ * Note: We used to have a set of conditions under which we would
+ * call fcoe_recv_frame directly, rather than queuing to the rx list
+ * as it could save a few cycles, but doing so is prohibited, as
+ * fcoe_recv_frame has several paths that may sleep, which is forbidden
+ * in softirq context.
*/
- if (fh->fh_type == FC_TYPE_FCP &&
- cpu == smp_processor_id() &&
- skb_queue_empty(&fps->fcoe_rx_list)) {
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
- fcoe_recv_frame(skb);
- } else {
- __skb_queue_tail(&fps->fcoe_rx_list, skb);
- if (fps->fcoe_rx_list.qlen == 1)
- wake_up_process(fps->thread);
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
- }
+ __skb_queue_tail(&fps->fcoe_rx_list, skb);
+ if (fps->thread->state == TASK_INTERRUPTIBLE)
+ wake_up_process(fps->thread);
+ spin_unlock(&fps->fcoe_rx_list.lock);
return 0;
err:
@@ -1797,23 +1790,29 @@ static int fcoe_percpu_receive_thread(void *arg)
{
struct fcoe_percpu_s *p = arg;
struct sk_buff *skb;
+ struct sk_buff_head tmp;
+
+ skb_queue_head_init(&tmp);
set_user_nice(current, -20);
while (!kthread_should_stop()) {
spin_lock_bh(&p->fcoe_rx_list.lock);
- while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) {
+ skb_queue_splice_init(&p->fcoe_rx_list, &tmp);
+ spin_unlock_bh(&p->fcoe_rx_list.lock);
+
+ while ((skb = __skb_dequeue(&tmp)) != NULL)
+ fcoe_recv_frame(skb);
+
+ spin_lock_bh(&p->fcoe_rx_list.lock);
+ if (!skb_queue_len(&p->fcoe_rx_list)) {
set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_bh(&p->fcoe_rx_list.lock);
schedule();
set_current_state(TASK_RUNNING);
- if (kthread_should_stop())
- return 0;
- spin_lock_bh(&p->fcoe_rx_list.lock);
- }
- spin_unlock_bh(&p->fcoe_rx_list.lock);
- fcoe_recv_frame(skb);
+ } else
+ spin_unlock_bh(&p->fcoe_rx_list.lock);
}
return 0;
}
@@ -2187,8 +2186,12 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
/* start FIP Discovery and FLOGI */
lport->boot_time = jiffies;
fc_fabric_login(lport);
- if (!fcoe_link_ok(lport))
+ if (!fcoe_link_ok(lport)) {
+ rtnl_unlock();
fcoe_ctlr_link_up(&fcoe->ctlr);
+ mutex_unlock(&fcoe_config_mutex);
+ return rc;
+ }
out_nodev:
rtnl_unlock();
@@ -2261,31 +2264,14 @@ static int fcoe_link_ok(struct fc_lport *lport)
static void fcoe_percpu_clean(struct fc_lport *lport)
{
struct fcoe_percpu_s *pp;
- struct fcoe_rcv_info *fr;
- struct sk_buff_head *list;
- struct sk_buff *skb, *next;
- struct sk_buff *head;
+ struct sk_buff *skb;
unsigned int cpu;
for_each_possible_cpu(cpu) {
pp = &per_cpu(fcoe_percpu, cpu);
- spin_lock_bh(&pp->fcoe_rx_list.lock);
- list = &pp->fcoe_rx_list;
- head = list->next;
- for (skb = head; skb != (struct sk_buff *)list;
- skb = next) {
- next = skb->next;
- fr = fcoe_dev_from_skb(skb);
- if (fr->fr_dev == lport) {
- __skb_unlink(skb, list);
- kfree_skb(skb);
- }
- }
- if (!pp->thread || !cpu_online(cpu)) {
- spin_unlock_bh(&pp->fcoe_rx_list.lock);
+ if (!pp->thread || !cpu_online(cpu))
continue;
- }
skb = dev_alloc_skb(0);
if (!skb) {
@@ -2294,6 +2280,7 @@ static void fcoe_percpu_clean(struct fc_lport *lport)
}
skb->destructor = fcoe_percpu_flush_done;
+ spin_lock_bh(&pp->fcoe_rx_list.lock);
__skb_queue_tail(&pp->fcoe_rx_list, skb);
if (pp->fcoe_rx_list.qlen == 1)
wake_up_process(pp->thread);
diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c
index e7522dc..249a106 100644
--- a/drivers/scsi/fcoe/fcoe_ctlr.c
+++ b/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -242,7 +242,7 @@ static void fcoe_ctlr_announce(struct fcoe_ctlr *fip)
printk(KERN_INFO "libfcoe: host%d: FIP selected "
"Fibre-Channel Forwarder MAC %pM\n",
fip->lp->host->host_no, sel->fcf_mac);
- memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
+ memcpy(fip->dest_addr, sel->fcoe_mac, ETH_ALEN);
fip->map_dest = 0;
}
unlock:
@@ -824,6 +824,7 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
memcpy(fcf->fcf_mac,
((struct fip_mac_desc *)desc)->fd_mac,
ETH_ALEN);
+ memcpy(fcf->fcoe_mac, fcf->fcf_mac, ETH_ALEN);
if (!is_valid_ether_addr(fcf->fcf_mac)) {
LIBFCOE_FIP_DBG(fip,
"Invalid MAC addr %pM in FIP adv\n",
@@ -1013,6 +1014,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
struct fip_desc *desc;
struct fip_encaps *els;
struct fcoe_dev_stats *stats;
+ struct fcoe_fcf *sel;
enum fip_desc_type els_dtype = 0;
u8 els_op;
u8 sub;
@@ -1040,7 +1042,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
goto drop;
/* Drop ELS if there are duplicate critical descriptors */
if (desc->fip_dtype < 32) {
- if (desc_mask & 1U << desc->fip_dtype) {
+ if ((desc->fip_dtype != FIP_DT_MAC) &&
+ (desc_mask & 1U << desc->fip_dtype)) {
LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
"Descriptors in FIP ELS\n");
goto drop;
@@ -1049,17 +1052,32 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
}
switch (desc->fip_dtype) {
case FIP_DT_MAC:
+ sel = fip->sel_fcf;
if (desc_cnt == 1) {
LIBFCOE_FIP_DBG(fip, "FIP descriptors "
"received out of order\n");
goto drop;
}
+ /*
+ * Some switch implementations send two MAC descriptors,
+ * with first MAC(granted_mac) being the FPMA, and the
+ * second one(fcoe_mac) is used as destination address
+ * for sending/receiving FCoE packets. FIP traffic is
+ * sent using fip_mac. For regular switches, both
+ * fip_mac and fcoe_mac would be the same.
+ */
+ if (desc_cnt == 2)
+ memcpy(granted_mac,
+ ((struct fip_mac_desc *)desc)->fd_mac,
+ ETH_ALEN);
if (dlen != sizeof(struct fip_mac_desc))
goto len_err;
- memcpy(granted_mac,
- ((struct fip_mac_desc *)desc)->fd_mac,
- ETH_ALEN);
+
+ if ((desc_cnt == 3) && (sel))
+ memcpy(sel->fcoe_mac,
+ ((struct fip_mac_desc *)desc)->fd_mac,
+ ETH_ALEN);
break;
case FIP_DT_FLOGI:
case FIP_DT_FDISC:
@@ -1273,11 +1291,6 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
* No Vx_Port description. Clear all NPIV ports,
* followed by physical port
*/
- mutex_lock(&lport->lp_mutex);
- list_for_each_entry(vn_port, &lport->vports, list)
- fc_lport_reset(vn_port);
- mutex_unlock(&lport->lp_mutex);
-
mutex_lock(&fip->ctlr_mutex);
per_cpu_ptr(lport->dev_stats,
get_cpu())->VLinkFailureCount++;
@@ -1285,6 +1298,11 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
fcoe_ctlr_reset(fip);
mutex_unlock(&fip->ctlr_mutex);
+ mutex_lock(&lport->lp_mutex);
+ list_for_each_entry(vn_port, &lport->vports, list)
+ fc_lport_reset(vn_port);
+ mutex_unlock(&lport->lp_mutex);
+
fc_lport_reset(fip->lp);
fcoe_ctlr_solicit(fip, NULL);
} else {
diff --git a/drivers/scsi/fd_mcs.c b/drivers/scsi/fd_mcs.c
index a2c6135..53bfcaa 100644
--- a/drivers/scsi/fd_mcs.c
+++ b/drivers/scsi/fd_mcs.c
@@ -93,7 +93,6 @@
#include <linux/mca-legacy.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "scsi.h"
#include <scsi/scsi_host.h>
diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c
index 643f6d5..1a2a1e5 100644
--- a/drivers/scsi/fdomain.c
+++ b/drivers/scsi/fdomain.c
@@ -282,7 +282,6 @@
#include <linux/slab.h>
#include <scsi/scsicam.h>
-#include <asm/system.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c
index 81182ba..1a5954f 100644
--- a/drivers/scsi/g_NCR5380.c
+++ b/drivers/scsi/g_NCR5380.c
@@ -100,7 +100,6 @@
#undef NCR5380_STAT_LIMIT
#endif
-#include <asm/system.h>
#include <asm/io.h>
#include <linux/signal.h>
#include <linux/blkdev.h>
diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
index d42ec92..5d72274 100644
--- a/drivers/scsi/gdth.c
+++ b/drivers/scsi/gdth.c
@@ -129,7 +129,6 @@
#include <linux/reboot.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/spinlock.h>
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
index 67fc8ff..cd09132 100644
--- a/drivers/scsi/ibmmca.c
+++ b/drivers/scsi/ibmmca.c
@@ -32,7 +32,6 @@
#include <linux/spinlock.h>
#include <linux/init.h>
-#include <asm/system.h>
#include <asm/io.h>
#include "scsi.h"
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index bdfa223..134a0ae 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -4890,11 +4890,8 @@ static struct vio_driver ibmvfc_driver = {
.probe = ibmvfc_probe,
.remove = ibmvfc_remove,
.get_desired_dma = ibmvfc_get_desired_dma,
- .driver = {
- .name = IBMVFC_NAME,
- .owner = THIS_MODULE,
- .pm = &ibmvfc_pm_ops,
- }
+ .name = IBMVFC_NAME,
+ .pm = &ibmvfc_pm_ops,
};
static struct fc_function_template ibmvfc_transport_functions = {
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index e984951..3a6c474 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -2061,11 +2061,8 @@ static struct vio_driver ibmvscsi_driver = {
.probe = ibmvscsi_probe,
.remove = ibmvscsi_remove,
.get_desired_dma = ibmvscsi_get_desired_dma,
- .driver = {
- .name = "ibmvscsi",
- .owner = THIS_MODULE,
- .pm = &ibmvscsi_pm_ops,
- }
+ .name = "ibmvscsi",
+ .pm = &ibmvscsi_pm_ops,
};
static struct srp_function_template ibmvscsi_transport_functions = {
diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c
index 2256bab..aa7ed81 100644
--- a/drivers/scsi/ibmvscsi/ibmvstgt.c
+++ b/drivers/scsi/ibmvscsi/ibmvstgt.c
@@ -918,10 +918,7 @@ static struct vio_driver ibmvstgt_driver = {
.id_table = ibmvstgt_device_table,
.probe = ibmvstgt_probe,
.remove = ibmvstgt_remove,
- .driver = {
- .name = "ibmvscsis",
- .owner = THIS_MODULE,
- }
+ .name = "ibmvscsis",
};
static int get_system_info(void)
diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c
index 112f1be..deb5b6d 100644
--- a/drivers/scsi/in2000.c
+++ b/drivers/scsi/in2000.c
@@ -123,7 +123,6 @@
#include <linux/stat.h>
#include <asm/io.h>
-#include <asm/system.h>
#include "scsi.h"
#include <scsi/scsi_host.h>
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index cdfe5a1..e002cd4 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -104,7 +104,9 @@ static DEFINE_SPINLOCK(ipr_driver_lock);
static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
{ /* Gemstone, Citrine, Obsidian, and Obsidian-E */
.mailbox = 0x0042C,
+ .max_cmds = 100,
.cache_line_size = 0x20,
+ .clear_isr = 1,
{
.set_interrupt_mask_reg = 0x0022C,
.clr_interrupt_mask_reg = 0x00230,
@@ -126,7 +128,9 @@ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
},
{ /* Snipe and Scamp */
.mailbox = 0x0052C,
+ .max_cmds = 100,
.cache_line_size = 0x20,
+ .clear_isr = 1,
{
.set_interrupt_mask_reg = 0x00288,
.clr_interrupt_mask_reg = 0x0028C,
@@ -148,7 +152,9 @@ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = {
},
{ /* CRoC */
.mailbox = 0x00044,
+ .max_cmds = 1000,
.cache_line_size = 0x20,
+ .clear_isr = 0,
{
.set_interrupt_mask_reg = 0x00010,
.clr_interrupt_mask_reg = 0x00018,
@@ -847,8 +853,6 @@ static void ipr_do_req(struct ipr_cmnd *ipr_cmd,
ipr_trc_hook(ipr_cmd, IPR_TRACE_START, 0);
- mb();
-
ipr_send_command(ipr_cmd);
}
@@ -982,8 +986,6 @@ static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type,
ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_IOA_RES_ADDR);
- mb();
-
ipr_send_command(ipr_cmd);
} else {
list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q);
@@ -4339,8 +4341,7 @@ static struct ipr_resource_entry *ipr_find_starget(struct scsi_target *starget)
list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
if ((res->bus == starget->channel) &&
- (res->target == starget->id) &&
- (res->lun == 0)) {
+ (res->target == starget->id)) {
return res;
}
}
@@ -4414,12 +4415,14 @@ static void ipr_target_destroy(struct scsi_target *starget)
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata;
if (ioa_cfg->sis64) {
- if (starget->channel == IPR_ARRAY_VIRTUAL_BUS)
- clear_bit(starget->id, ioa_cfg->array_ids);
- else if (starget->channel == IPR_VSET_VIRTUAL_BUS)
- clear_bit(starget->id, ioa_cfg->vset_ids);
- else if (starget->channel == 0)
- clear_bit(starget->id, ioa_cfg->target_ids);
+ if (!ipr_find_starget(starget)) {
+ if (starget->channel == IPR_ARRAY_VIRTUAL_BUS)
+ clear_bit(starget->id, ioa_cfg->array_ids);
+ else if (starget->channel == IPR_VSET_VIRTUAL_BUS)
+ clear_bit(starget->id, ioa_cfg->vset_ids);
+ else if (starget->channel == 0)
+ clear_bit(starget->id, ioa_cfg->target_ids);
+ }
}
if (sata_port) {
@@ -5048,12 +5051,14 @@ static irqreturn_t ipr_handle_other_interrupt(struct ipr_ioa_cfg *ioa_cfg,
del_timer(&ioa_cfg->reset_cmd->timer);
ipr_reset_ioa_job(ioa_cfg->reset_cmd);
} else if ((int_reg & IPR_PCII_HRRQ_UPDATED) == int_reg) {
- if (ipr_debug && printk_ratelimit())
- dev_err(&ioa_cfg->pdev->dev,
- "Spurious interrupt detected. 0x%08X\n", int_reg);
- writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg32);
- int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
- return IRQ_NONE;
+ if (ioa_cfg->clear_isr) {
+ if (ipr_debug && printk_ratelimit())
+ dev_err(&ioa_cfg->pdev->dev,
+ "Spurious interrupt detected. 0x%08X\n", int_reg);
+ writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg32);
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
+ return IRQ_NONE;
+ }
} else {
if (int_reg & IPR_PCII_IOA_UNIT_CHECKED)
ioa_cfg->ioa_unit_checked = 1;
@@ -5153,6 +5158,9 @@ static irqreturn_t ipr_isr(int irq, void *devp)
}
}
+ if (ipr_cmd && !ioa_cfg->clear_isr)
+ break;
+
if (ipr_cmd != NULL) {
/* Clear the PCI interrupt */
num_hrrq = 0;
@@ -5854,14 +5862,12 @@ static int ipr_queuecommand_lck(struct scsi_cmnd *scsi_cmd,
rc = ipr_build_ioadl(ioa_cfg, ipr_cmd);
}
- if (likely(rc == 0)) {
- mb();
- ipr_send_command(ipr_cmd);
- } else {
- list_move_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
- return SCSI_MLQUEUE_HOST_BUSY;
+ if (unlikely(rc != 0)) {
+ list_move_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
+ return SCSI_MLQUEUE_HOST_BUSY;
}
+ ipr_send_command(ipr_cmd);
return 0;
}
@@ -6239,8 +6245,6 @@ static unsigned int ipr_qc_issue(struct ata_queued_cmd *qc)
return AC_ERR_INVALID;
}
- mb();
-
ipr_send_command(ipr_cmd);
return 0;
@@ -8277,6 +8281,10 @@ static void ipr_free_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
if (ioa_cfg->ipr_cmd_pool)
pci_pool_destroy (ioa_cfg->ipr_cmd_pool);
+ kfree(ioa_cfg->ipr_cmnd_list);
+ kfree(ioa_cfg->ipr_cmnd_list_dma);
+ ioa_cfg->ipr_cmnd_list = NULL;
+ ioa_cfg->ipr_cmnd_list_dma = NULL;
ioa_cfg->ipr_cmd_pool = NULL;
}
@@ -8352,11 +8360,19 @@ static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
int i;
ioa_cfg->ipr_cmd_pool = pci_pool_create (IPR_NAME, ioa_cfg->pdev,
- sizeof(struct ipr_cmnd), 16, 0);
+ sizeof(struct ipr_cmnd), 512, 0);
if (!ioa_cfg->ipr_cmd_pool)
return -ENOMEM;
+ ioa_cfg->ipr_cmnd_list = kcalloc(IPR_NUM_CMD_BLKS, sizeof(struct ipr_cmnd *), GFP_KERNEL);
+ ioa_cfg->ipr_cmnd_list_dma = kcalloc(IPR_NUM_CMD_BLKS, sizeof(dma_addr_t), GFP_KERNEL);
+
+ if (!ioa_cfg->ipr_cmnd_list || !ioa_cfg->ipr_cmnd_list_dma) {
+ ipr_free_cmd_blks(ioa_cfg);
+ return -ENOMEM;
+ }
+
for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
ipr_cmd = pci_pool_alloc (ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
@@ -8584,6 +8600,7 @@ static void __devinit ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg,
host->max_channel = IPR_MAX_BUS_TO_SCAN;
host->unique_id = host->host_no;
host->max_cmd_len = IPR_MAX_CDB_LEN;
+ host->can_queue = ioa_cfg->max_cmds;
pci_set_drvdata(pdev, ioa_cfg);
p = &ioa_cfg->chip_cfg->regs;
@@ -8768,6 +8785,8 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
/* set SIS 32 or SIS 64 */
ioa_cfg->sis64 = ioa_cfg->ipr_chip->sis_type == IPR_SIS64 ? 1 : 0;
ioa_cfg->chip_cfg = ioa_cfg->ipr_chip->cfg;
+ ioa_cfg->clear_isr = ioa_cfg->chip_cfg->clear_isr;
+ ioa_cfg->max_cmds = ioa_cfg->chip_cfg->max_cmds;
if (ipr_transop_timeout)
ioa_cfg->transop_timeout = ipr_transop_timeout;
diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h
index f94eaee..153b8bd 100644
--- a/drivers/scsi/ipr.h
+++ b/drivers/scsi/ipr.h
@@ -38,8 +38,8 @@
/*
* Literals
*/
-#define IPR_DRIVER_VERSION "2.5.2"
-#define IPR_DRIVER_DATE "(April 27, 2011)"
+#define IPR_DRIVER_VERSION "2.5.3"
+#define IPR_DRIVER_DATE "(March 10, 2012)"
/*
* IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding
@@ -53,7 +53,7 @@
* IPR_NUM_BASE_CMD_BLKS: This defines the maximum number of
* ops the mid-layer can send to the adapter.
*/
-#define IPR_NUM_BASE_CMD_BLKS 100
+#define IPR_NUM_BASE_CMD_BLKS (ioa_cfg->max_cmds)
#define PCI_DEVICE_ID_IBM_OBSIDIAN_E 0x0339
@@ -153,7 +153,7 @@
#define IPR_NUM_INTERNAL_CMD_BLKS (IPR_NUM_HCAMS + \
((IPR_NUM_RESET_RELOAD_RETRIES + 1) * 2) + 4)
-#define IPR_MAX_COMMANDS IPR_NUM_BASE_CMD_BLKS
+#define IPR_MAX_COMMANDS 100
#define IPR_NUM_CMD_BLKS (IPR_NUM_BASE_CMD_BLKS + \
IPR_NUM_INTERNAL_CMD_BLKS)
@@ -1305,7 +1305,9 @@ struct ipr_interrupts {
struct ipr_chip_cfg_t {
u32 mailbox;
+ u16 max_cmds;
u8 cache_line_size;
+ u8 clear_isr;
struct ipr_interrupt_offsets regs;
};
@@ -1388,6 +1390,7 @@ struct ipr_ioa_cfg {
u8 sis64:1;
u8 dump_timeout:1;
u8 cfg_locked:1;
+ u8 clear_isr:1;
u8 revid;
@@ -1501,8 +1504,9 @@ struct ipr_ioa_cfg {
struct ata_host ata_host;
char ipr_cmd_label[8];
#define IPR_CMD_LABEL "ipr_cmd"
- struct ipr_cmnd *ipr_cmnd_list[IPR_NUM_CMD_BLKS];
- dma_addr_t ipr_cmnd_list_dma[IPR_NUM_CMD_BLKS];
+ u32 max_cmds;
+ struct ipr_cmnd **ipr_cmnd_list;
+ dma_addr_t *ipr_cmnd_list_dma;
}; /* struct ipr_ioa_cfg */
struct ipr_cmnd {
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 630291f..aceffad 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -2263,7 +2263,18 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
mp->class = class;
/* adjust em exch xid range for offload */
mp->min_xid = min_xid;
- mp->max_xid = max_xid;
+
+ /* reduce range so per cpu pool fits into PCPU_MIN_UNIT_SIZE pool */
+ pool_exch_range = (PCPU_MIN_UNIT_SIZE - sizeof(*pool)) /
+ sizeof(struct fc_exch *);
+ if ((max_xid - min_xid + 1) / (fc_cpu_mask + 1) > pool_exch_range) {
+ mp->max_xid = pool_exch_range * (fc_cpu_mask + 1) +
+ min_xid - 1;
+ } else {
+ mp->max_xid = max_xid;
+ pool_exch_range = (mp->max_xid - mp->min_xid + 1) /
+ (fc_cpu_mask + 1);
+ }
mp->ep_pool = mempool_create_slab_pool(2, fc_em_cachep);
if (!mp->ep_pool)
@@ -2274,7 +2285,6 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
* divided across all cpus. The exch pointers array memory is
* allocated for exch range per pool.
*/
- pool_exch_range = (mp->max_xid - mp->min_xid + 1) / (fc_cpu_mask + 1);
mp->pool_max_index = pool_exch_range - 1;
/*
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index bd5d31d..ef9560d 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -1743,8 +1743,16 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
mfs = ntohs(flp->fl_csp.sp_bb_data) &
FC_SP_BB_DATA_MASK;
if (mfs >= FC_SP_MIN_MAX_PAYLOAD &&
- mfs < lport->mfs)
+ mfs <= lport->mfs) {
lport->mfs = mfs;
+ fc_host_maxframe_size(lport->host) = mfs;
+ } else {
+ FC_LPORT_DBG(lport, "FLOGI bad mfs:%hu response, "
+ "lport->mfs:%hu\n", mfs, lport->mfs);
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
csp_flags = ntohs(flp->fl_csp.sp_features);
r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov);
e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov);
diff --git a/drivers/scsi/lpfc/Makefile b/drivers/scsi/lpfc/Makefile
index 88928f0..fe5d396 100644
--- a/drivers/scsi/lpfc/Makefile
+++ b/drivers/scsi/lpfc/Makefile
@@ -1,7 +1,7 @@
#/*******************************************************************
# * This file is part of the Emulex Linux Device Driver for *
# * Fibre Channel Host Bus Adapters. *
-# * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+# * Copyright (C) 2004-2012 Emulex. All rights reserved. *
# * EMULEX and SLI are trademarks of Emulex. *
# * www.emulex.com *
# * *
@@ -22,6 +22,8 @@
ccflags-$(GCOV) := -fprofile-arcs -ftest-coverage
ccflags-$(GCOV) += -O0
+ccflags-y += -Werror
+
obj-$(CONFIG_SCSI_LPFC) := lpfc.o
lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o lpfc_hbadisc.o \
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 5fc044f..3a1ffdd 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2012 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -840,6 +840,8 @@ struct lpfc_hba {
struct dentry *debug_dumpData; /* BlockGuard BPL */
struct dentry *debug_dumpDif; /* BlockGuard BPL */
struct dentry *debug_InjErrLBA; /* LBA to inject errors at */
+ struct dentry *debug_InjErrNPortID; /* NPortID to inject errors at */
+ struct dentry *debug_InjErrWWPN; /* WWPN to inject errors at */
struct dentry *debug_writeGuard; /* inject write guard_tag errors */
struct dentry *debug_writeApp; /* inject write app_tag errors */
struct dentry *debug_writeRef; /* inject write ref_tag errors */
@@ -854,6 +856,8 @@ struct lpfc_hba {
uint32_t lpfc_injerr_rgrd_cnt;
uint32_t lpfc_injerr_rapp_cnt;
uint32_t lpfc_injerr_rref_cnt;
+ uint32_t lpfc_injerr_nportid;
+ struct lpfc_name lpfc_injerr_wwpn;
sector_t lpfc_injerr_lba;
#define LPFC_INJERR_LBA_OFF (sector_t)(-1)
@@ -908,6 +912,8 @@ struct lpfc_hba {
atomic_t fast_event_count;
uint32_t fcoe_eventtag;
uint32_t fcoe_eventtag_at_fcf_scan;
+ uint32_t fcoe_cvl_eventtag;
+ uint32_t fcoe_cvl_eventtag_attn;
struct lpfc_fcf fcf;
uint8_t fc_map[3];
uint8_t valid_vlan;
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 296ad5b..5eb2bc1 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2012 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -2575,7 +2575,7 @@ LPFC_VPORT_ATTR_HEX_RW(log_verbose, 0x0, 0x0, 0xffffffff,
# lpfc_enable_da_id: This turns on the DA_ID CT command that deregisters
# objects that have been registered with the nameserver after login.
*/
-LPFC_VPORT_ATTR_R(enable_da_id, 0, 0, 1,
+LPFC_VPORT_ATTR_R(enable_da_id, 1, 0, 1,
"Deregister nameserver objects before LOGO");
/*
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index 22e17be..5bdf2ee 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2007-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2007-2012 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -1010,25 +1010,35 @@ lpfc_debugfs_dif_err_read(struct file *file, char __user *buf,
{
struct dentry *dent = file->f_dentry;
struct lpfc_hba *phba = file->private_data;
- char cbuf[16];
+ char cbuf[32];
+ uint64_t tmp = 0;
int cnt = 0;
if (dent == phba->debug_writeGuard)
- cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_wgrd_cnt);
+ cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_wgrd_cnt);
else if (dent == phba->debug_writeApp)
- cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_wapp_cnt);
+ cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_wapp_cnt);
else if (dent == phba->debug_writeRef)
- cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_wref_cnt);
+ cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_wref_cnt);
else if (dent == phba->debug_readGuard)
- cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_rgrd_cnt);
+ cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_rgrd_cnt);
else if (dent == phba->debug_readApp)
- cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_rapp_cnt);
+ cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_rapp_cnt);
else if (dent == phba->debug_readRef)
- cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_rref_cnt);
- else if (dent == phba->debug_InjErrLBA)
- cnt = snprintf(cbuf, 16, "0x%lx\n",
- (unsigned long) phba->lpfc_injerr_lba);
- else
+ cnt = snprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_rref_cnt);
+ else if (dent == phba->debug_InjErrNPortID)
+ cnt = snprintf(cbuf, 32, "0x%06x\n", phba->lpfc_injerr_nportid);
+ else if (dent == phba->debug_InjErrWWPN) {
+ memcpy(&tmp, &phba->lpfc_injerr_wwpn, sizeof(struct lpfc_name));
+ tmp = cpu_to_be64(tmp);
+ cnt = snprintf(cbuf, 32, "0x%016llx\n", tmp);
+ } else if (dent == phba->debug_InjErrLBA) {
+ if (phba->lpfc_injerr_lba == (sector_t)(-1))
+ cnt = snprintf(cbuf, 32, "off\n");
+ else
+ cnt = snprintf(cbuf, 32, "0x%llx\n",
+ (uint64_t) phba->lpfc_injerr_lba);
+ } else
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0547 Unknown debugfs error injection entry\n");
@@ -1042,7 +1052,7 @@ lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf,
struct dentry *dent = file->f_dentry;
struct lpfc_hba *phba = file->private_data;
char dstbuf[32];
- unsigned long tmp;
+ uint64_t tmp = 0;
int size;
memset(dstbuf, 0, 32);
@@ -1050,7 +1060,12 @@ lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf,
if (copy_from_user(dstbuf, buf, size))
return 0;
- if (strict_strtoul(dstbuf, 0, &tmp))
+ if (dent == phba->debug_InjErrLBA) {
+ if ((buf[0] == 'o') && (buf[1] == 'f') && (buf[2] == 'f'))
+ tmp = (uint64_t)(-1);
+ }
+
+ if ((tmp == 0) && (kstrtoull(dstbuf, 0, &tmp)))
return 0;
if (dent == phba->debug_writeGuard)
@@ -1067,7 +1082,12 @@ lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf,
phba->lpfc_injerr_rref_cnt = (uint32_t)tmp;
else if (dent == phba->debug_InjErrLBA)
phba->lpfc_injerr_lba = (sector_t)tmp;
- else
+ else if (dent == phba->debug_InjErrNPortID)
+ phba->lpfc_injerr_nportid = (uint32_t)(tmp & Mask_DID);
+ else if (dent == phba->debug_InjErrWWPN) {
+ tmp = cpu_to_be64(tmp);
+ memcpy(&phba->lpfc_injerr_wwpn, &tmp, sizeof(struct lpfc_name));
+ } else
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0548 Unknown debugfs error injection entry\n");
@@ -3949,6 +3969,28 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
}
phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+ snprintf(name, sizeof(name), "InjErrNPortID");
+ phba->debug_InjErrNPortID =
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+ phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_dif_err);
+ if (!phba->debug_InjErrNPortID) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "0809 Cannot create debugfs InjErrNPortID\n");
+ goto debug_failed;
+ }
+
+ snprintf(name, sizeof(name), "InjErrWWPN");
+ phba->debug_InjErrWWPN =
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+ phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_dif_err);
+ if (!phba->debug_InjErrWWPN) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "0810 Cannot create debugfs InjErrWWPN\n");
+ goto debug_failed;
+ }
+
snprintf(name, sizeof(name), "writeGuardInjErr");
phba->debug_writeGuard =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
@@ -4321,6 +4363,14 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
debugfs_remove(phba->debug_InjErrLBA); /* InjErrLBA */
phba->debug_InjErrLBA = NULL;
}
+ if (phba->debug_InjErrNPortID) { /* InjErrNPortID */
+ debugfs_remove(phba->debug_InjErrNPortID);
+ phba->debug_InjErrNPortID = NULL;
+ }
+ if (phba->debug_InjErrWWPN) {
+ debugfs_remove(phba->debug_InjErrWWPN); /* InjErrWWPN */
+ phba->debug_InjErrWWPN = NULL;
+ }
if (phba->debug_writeGuard) {
debugfs_remove(phba->debug_writeGuard); /* writeGuard */
phba->debug_writeGuard = NULL;
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 8db2fb3..3407b39 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2012 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -925,9 +925,17 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
* due to new FCF discovery
*/
if ((phba->hba_flag & HBA_FIP_SUPPORT) &&
- (phba->fcf.fcf_flag & FCF_DISCOVERY) &&
- !((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
- (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED))) {
+ (phba->fcf.fcf_flag & FCF_DISCOVERY)) {
+ if (phba->link_state < LPFC_LINK_UP)
+ goto stop_rr_fcf_flogi;
+ if ((phba->fcoe_cvl_eventtag_attn ==
+ phba->fcoe_cvl_eventtag) &&
+ (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+ (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED))
+ goto stop_rr_fcf_flogi;
+ else
+ phba->fcoe_cvl_eventtag_attn =
+ phba->fcoe_cvl_eventtag;
lpfc_printf_log(phba, KERN_WARNING, LOG_FIP | LOG_ELS,
"2611 FLOGI failed on FCF (x%x), "
"status:x%x/x%x, tmo:x%x, perform "
@@ -943,6 +951,7 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
goto out;
}
+stop_rr_fcf_flogi:
/* FLOGI failure */
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
"2858 FLOGI failure Status:x%x/x%x TMO:x%x\n",
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 343d87b..b507536 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2012 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -2843,7 +2843,14 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
struct lpfc_vport *vport = mboxq->vport;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- if (mboxq->u.mb.mbxStatus) {
+ /*
+ * VFI not supported for interface type 0, so ignore any mailbox
+ * error (except VFI in use) and continue with the discovery.
+ */
+ if (mboxq->u.mb.mbxStatus &&
+ (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ LPFC_SLI_INTF_IF_TYPE_0) &&
+ mboxq->u.mb.mbxStatus != MBX_VFI_IN_USE) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX,
"2018 REG_VFI mbxStatus error x%x "
"HBA state x%x\n",
@@ -5673,14 +5680,13 @@ lpfc_fcf_inuse(struct lpfc_hba *phba)
ret = 1;
spin_unlock_irq(shost->host_lock);
goto out;
- } else {
+ } else if (ndlp->nlp_flag & NLP_RPI_REGISTERED) {
+ ret = 1;
lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
- "2624 RPI %x DID %x flg %x still "
- "logged in\n",
- ndlp->nlp_rpi, ndlp->nlp_DID,
- ndlp->nlp_flag);
- if (ndlp->nlp_flag & NLP_RPI_REGISTERED)
- ret = 1;
+ "2624 RPI %x DID %x flag %x "
+ "still logged in\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID,
+ ndlp->nlp_flag);
}
}
spin_unlock_irq(shost->host_lock);
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 9e2b9b2..91f0976 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2009 Emulex. All rights reserved. *
+ * Copyright (C) 2009-2012 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -338,6 +338,12 @@ struct lpfc_cqe {
#define CQE_CODE_XRI_ABORTED 0x5
#define CQE_CODE_RECEIVE_V1 0x9
+/*
+ * Define mask value for xri_aborted and wcqe completed CQE extended status.
+ * Currently, extended status is limited to 9 bits (0x0 -> 0x103) .
+ */
+#define WCQE_PARAM_MASK 0x1FF;
+
/* completion queue entry for wqe completions */
struct lpfc_wcqe_complete {
uint32_t word0;
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index b38f99f..9598fdc 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2012 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -2704,16 +2704,14 @@ lpfc_offline_prep(struct lpfc_hba * phba)
}
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_NPR_ADISC;
-
+ spin_unlock_irq(shost->host_lock);
/*
* Whenever an SLI4 port goes offline, free the
- * RPI. A new RPI when the adapter port comes
- * back online.
+ * RPI. Get a new RPI when the adapter port
+ * comes back online.
*/
if (phba->sli_rev == LPFC_SLI_REV4)
lpfc_sli4_free_rpi(phba, ndlp->nlp_rpi);
-
- spin_unlock_irq(shost->host_lock);
lpfc_unreg_rpi(vports[i], ndlp);
}
}
@@ -2786,9 +2784,13 @@ lpfc_scsi_buf_update(struct lpfc_hba *phba)
spin_lock_irq(&phba->hbalock);
spin_lock(&phba->scsi_buf_list_lock);
- list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list, list)
+ list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list, list) {
sb->cur_iocbq.sli4_xritag =
phba->sli4_hba.xri_ids[sb->cur_iocbq.sli4_lxritag];
+ set_bit(sb->cur_iocbq.sli4_lxritag, phba->sli4_hba.xri_bmask);
+ phba->sli4_hba.max_cfg_param.xri_used++;
+ phba->sli4_hba.xri_count++;
+ }
spin_unlock(&phba->scsi_buf_list_lock);
spin_unlock_irq(&phba->hbalock);
return 0;
@@ -3723,6 +3725,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
break;
case LPFC_FIP_EVENT_TYPE_FCF_DEAD:
+ phba->fcoe_cvl_eventtag = acqe_fip->event_tag;
lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY,
"2549 FCF (x%x) disconnected from network, "
"tag:x%x\n", acqe_fip->index, acqe_fip->event_tag);
@@ -3784,6 +3787,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
}
break;
case LPFC_FIP_EVENT_TYPE_CVL:
+ phba->fcoe_cvl_eventtag = acqe_fip->event_tag;
lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY,
"2718 Clear Virtual Link Received for VPI 0x%x"
" tag 0x%x\n", acqe_fip->index, acqe_fip->event_tag);
@@ -5226,8 +5230,7 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba)
* rpi is normalized to a zero base because the physical rpi is
* port based.
*/
- curr_rpi_range = phba->sli4_hba.next_rpi -
- phba->sli4_hba.max_cfg_param.rpi_base;
+ curr_rpi_range = phba->sli4_hba.next_rpi;
spin_unlock_irq(&phba->hbalock);
/*
@@ -5818,10 +5821,9 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba)
readl(phba->sli4_hba.u.if_type2.
ERR2regaddr);
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2888 Port Error Detected "
- "during POST: "
- "port status reg 0x%x, "
- "port_smphr reg 0x%x, "
+ "2888 Unrecoverable port error "
+ "following POST: port status reg "
+ "0x%x, port_smphr reg 0x%x, "
"error 1=0x%x, error 2=0x%x\n",
reg_data.word0,
portsmphr_reg.word0,
@@ -6142,7 +6144,6 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
phba->sli4_hba.next_xri = phba->sli4_hba.max_cfg_param.xri_base;
phba->vpi_base = phba->sli4_hba.max_cfg_param.vpi_base;
phba->vfi_base = phba->sli4_hba.max_cfg_param.vfi_base;
- phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.rpi_base;
phba->max_vpi = (phba->sli4_hba.max_cfg_param.max_vpi > 0) ?
(phba->sli4_hba.max_cfg_param.max_vpi - 1) : 0;
phba->max_vports = phba->max_vpi;
@@ -7231,6 +7232,7 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
uint32_t rdy_chk, num_resets = 0, reset_again = 0;
union lpfc_sli4_cfg_shdr *shdr;
struct lpfc_register reg_data;
+ uint16_t devid;
if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
switch (if_type) {
@@ -7277,7 +7279,9 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
LPFC_SLIPORT_INIT_PORT);
writel(reg_data.word0, phba->sli4_hba.u.if_type2.
CTRLregaddr);
-
+ /* flush */
+ pci_read_config_word(phba->pcidev,
+ PCI_DEVICE_ID, &devid);
/*
* Poll the Port Status Register and wait for RDY for
* up to 10 seconds. If the port doesn't respond, treat
@@ -7315,11 +7319,10 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
phba->work_status[1] = readl(
phba->sli4_hba.u.if_type2.ERR2regaddr);
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "2890 Port Error Detected "
- "during Port Reset: "
- "port status reg 0x%x, "
+ "2890 Port error detected during port "
+ "reset(%d): port status reg 0x%x, "
"error 1=0x%x, error 2=0x%x\n",
- reg_data.word0,
+ num_resets, reg_data.word0,
phba->work_status[0],
phba->work_status[1]);
rc = -ENODEV;
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 7b6b2aa..15ca2a9 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2009 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2012 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -440,11 +440,15 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
spin_unlock_irq(shost->host_lock);
stat.un.b.lsRjtRsnCode = LSRJT_INVALID_CMD;
stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
- lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,
+ rc = lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,
ndlp, mbox);
+ if (rc)
+ mempool_free(mbox, phba->mbox_mem_pool);
return 1;
}
- lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox);
+ rc = lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox);
+ if (rc)
+ mempool_free(mbox, phba->mbox_mem_pool);
return 1;
out:
stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index efc055b..88f3a83 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -39,8 +39,8 @@
#include "lpfc_sli4.h"
#include "lpfc_nl.h"
#include "lpfc_disc.h"
-#include "lpfc_scsi.h"
#include "lpfc.h"
+#include "lpfc_scsi.h"
#include "lpfc_logmsg.h"
#include "lpfc_crtn.h"
#include "lpfc_vport.h"
@@ -51,13 +51,19 @@
int _dump_buf_done;
static char *dif_op_str[] = {
- "SCSI_PROT_NORMAL",
- "SCSI_PROT_READ_INSERT",
- "SCSI_PROT_WRITE_STRIP",
- "SCSI_PROT_READ_STRIP",
- "SCSI_PROT_WRITE_INSERT",
- "SCSI_PROT_READ_PASS",
- "SCSI_PROT_WRITE_PASS",
+ "PROT_NORMAL",
+ "PROT_READ_INSERT",
+ "PROT_WRITE_STRIP",
+ "PROT_READ_STRIP",
+ "PROT_WRITE_INSERT",
+ "PROT_READ_PASS",
+ "PROT_WRITE_PASS",
+};
+
+static char *dif_grd_str[] = {
+ "NO_GUARD",
+ "DIF_CRC",
+ "DIX_IP",
};
struct scsi_dif_tuple {
@@ -1281,10 +1287,14 @@ lpfc_cmd_blksize(struct scsi_cmnd *sc)
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
-#define BG_ERR_INIT 1
-#define BG_ERR_TGT 2
-#define BG_ERR_SWAP 3
-#define BG_ERR_CHECK 4
+/* Return if if error injection is detected by Initiator */
+#define BG_ERR_INIT 0x1
+/* Return if if error injection is detected by Target */
+#define BG_ERR_TGT 0x2
+/* Return if if swapping CSUM<-->CRC is required for error injection */
+#define BG_ERR_SWAP 0x10
+/* Return if disabling Guard/Ref/App checking is required for error injection */
+#define BG_ERR_CHECK 0x20
/**
* lpfc_bg_err_inject - Determine if we should inject an error
@@ -1294,10 +1304,7 @@ lpfc_cmd_blksize(struct scsi_cmnd *sc)
* @apptag: (out) BlockGuard application tag for transmitted data
* @new_guard (in) Value to replace CRC with if needed
*
- * Returns (1) if error injection is detected by Initiator
- * Returns (2) if error injection is detected by Target
- * Returns (3) if swapping CSUM->CRC is required for error injection
- * Returns (4) disabling Guard/Ref/App checking is required for error injection
+ * Returns BG_ERR_* bit mask or 0 if request ignored
**/
static int
lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
@@ -1305,7 +1312,10 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
{
struct scatterlist *sgpe; /* s/g prot entry */
struct scatterlist *sgde; /* s/g data entry */
+ struct lpfc_scsi_buf *lpfc_cmd = NULL;
struct scsi_dif_tuple *src = NULL;
+ struct lpfc_nodelist *ndlp;
+ struct lpfc_rport_data *rdata;
uint32_t op = scsi_get_prot_op(sc);
uint32_t blksize;
uint32_t numblks;
@@ -1318,8 +1328,9 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
sgpe = scsi_prot_sglist(sc);
sgde = scsi_sglist(sc);
-
lba = scsi_get_lba(sc);
+
+ /* First check if we need to match the LBA */
if (phba->lpfc_injerr_lba != LPFC_INJERR_LBA_OFF) {
blksize = lpfc_cmd_blksize(sc);
numblks = (scsi_bufflen(sc) + blksize - 1) / blksize;
@@ -1334,66 +1345,123 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
sizeof(struct scsi_dif_tuple);
if (numblks < blockoff)
blockoff = numblks;
- src = (struct scsi_dif_tuple *)sg_virt(sgpe);
- src += blockoff;
}
}
+ /* Next check if we need to match the remote NPortID or WWPN */
+ rdata = sc->device->hostdata;
+ if (rdata && rdata->pnode) {
+ ndlp = rdata->pnode;
+
+ /* Make sure we have the right NPortID if one is specified */
+ if (phba->lpfc_injerr_nportid &&
+ (phba->lpfc_injerr_nportid != ndlp->nlp_DID))
+ return 0;
+
+ /*
+ * Make sure we have the right WWPN if one is specified.
+ * wwn[0] should be a non-zero NAA in a good WWPN.
+ */
+ if (phba->lpfc_injerr_wwpn.u.wwn[0] &&
+ (memcmp(&ndlp->nlp_portname, &phba->lpfc_injerr_wwpn,
+ sizeof(struct lpfc_name)) != 0))
+ return 0;
+ }
+
+ /* Setup a ptr to the protection data if the SCSI host provides it */
+ if (sgpe) {
+ src = (struct scsi_dif_tuple *)sg_virt(sgpe);
+ src += blockoff;
+ lpfc_cmd = (struct lpfc_scsi_buf *)sc->host_scribble;
+ }
+
/* Should we change the Reference Tag */
if (reftag) {
if (phba->lpfc_injerr_wref_cnt) {
switch (op) {
case SCSI_PROT_WRITE_PASS:
- if (blockoff && src) {
- /* Insert error in middle of the IO */
+ if (src) {
+ /*
+ * For WRITE_PASS, force the error
+ * to be sent on the wire. It should
+ * be detected by the Target.
+ * If blockoff != 0 error will be
+ * inserted in middle of the IO.
+ */
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
"9076 BLKGRD: Injecting reftag error: "
"write lba x%lx + x%x oldrefTag x%x\n",
(unsigned long)lba, blockoff,
- src->ref_tag);
+ be32_to_cpu(src->ref_tag));
/*
- * NOTE, this will change ref tag in
- * the memory location forever!
+ * Save the old ref_tag so we can
+ * restore it on completion.
*/
- src->ref_tag = 0xDEADBEEF;
+ if (lpfc_cmd) {
+ lpfc_cmd->prot_data_type =
+ LPFC_INJERR_REFTAG;
+ lpfc_cmd->prot_data_segment =
+ src;
+ lpfc_cmd->prot_data =
+ src->ref_tag;
+ }
+ src->ref_tag = cpu_to_be32(0xDEADBEEF);
phba->lpfc_injerr_wref_cnt--;
- phba->lpfc_injerr_lba =
- LPFC_INJERR_LBA_OFF;
- rc = BG_ERR_CHECK;
+ if (phba->lpfc_injerr_wref_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_TGT | BG_ERR_CHECK;
+
break;
}
/* Drop thru */
- case SCSI_PROT_WRITE_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
/*
- * For WRITE_STRIP and WRITE_PASS,
- * force the error on data
- * being copied from SLI-Host to SLI-Port.
+ * For WRITE_INSERT, force the error
+ * to be sent on the wire. It should be
+ * detected by the Target.
*/
+ /* DEADBEEF will be the reftag on the wire */
*reftag = 0xDEADBEEF;
phba->lpfc_injerr_wref_cnt--;
- phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
- rc = BG_ERR_INIT;
+ if (phba->lpfc_injerr_wref_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_TGT | BG_ERR_CHECK;
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9077 BLKGRD: Injecting reftag error: "
+ "9078 BLKGRD: Injecting reftag error: "
"write lba x%lx\n", (unsigned long)lba);
break;
- case SCSI_PROT_WRITE_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
/*
- * For WRITE_INSERT, force the
- * error to be sent on the wire. It should be
- * detected by the Target.
+ * For WRITE_STRIP and WRITE_PASS,
+ * force the error on data
+ * being copied from SLI-Host to SLI-Port.
*/
- /* DEADBEEF will be the reftag on the wire */
*reftag = 0xDEADBEEF;
phba->lpfc_injerr_wref_cnt--;
- phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
- rc = BG_ERR_TGT;
+ if (phba->lpfc_injerr_wref_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_INIT;
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9078 BLKGRD: Injecting reftag error: "
+ "9077 BLKGRD: Injecting reftag error: "
"write lba x%lx\n", (unsigned long)lba);
break;
}
@@ -1401,11 +1469,6 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
if (phba->lpfc_injerr_rref_cnt) {
switch (op) {
case SCSI_PROT_READ_INSERT:
- /*
- * For READ_INSERT, it doesn't make sense
- * to change the reftag.
- */
- break;
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_READ_PASS:
/*
@@ -1415,7 +1478,13 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
*/
*reftag = 0xDEADBEEF;
phba->lpfc_injerr_rref_cnt--;
- phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+ if (phba->lpfc_injerr_rref_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
rc = BG_ERR_INIT;
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
@@ -1431,56 +1500,87 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
if (phba->lpfc_injerr_wapp_cnt) {
switch (op) {
case SCSI_PROT_WRITE_PASS:
- if (blockoff && src) {
- /* Insert error in middle of the IO */
+ if (src) {
+ /*
+ * For WRITE_PASS, force the error
+ * to be sent on the wire. It should
+ * be detected by the Target.
+ * If blockoff != 0 error will be
+ * inserted in middle of the IO.
+ */
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
"9080 BLKGRD: Injecting apptag error: "
"write lba x%lx + x%x oldappTag x%x\n",
(unsigned long)lba, blockoff,
- src->app_tag);
+ be16_to_cpu(src->app_tag));
/*
- * NOTE, this will change app tag in
- * the memory location forever!
+ * Save the old app_tag so we can
+ * restore it on completion.
*/
- src->app_tag = 0xDEAD;
+ if (lpfc_cmd) {
+ lpfc_cmd->prot_data_type =
+ LPFC_INJERR_APPTAG;
+ lpfc_cmd->prot_data_segment =
+ src;
+ lpfc_cmd->prot_data =
+ src->app_tag;
+ }
+ src->app_tag = cpu_to_be16(0xDEAD);
phba->lpfc_injerr_wapp_cnt--;
- phba->lpfc_injerr_lba =
- LPFC_INJERR_LBA_OFF;
- rc = BG_ERR_CHECK;
+ if (phba->lpfc_injerr_wapp_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_TGT | BG_ERR_CHECK;
break;
}
/* Drop thru */
- case SCSI_PROT_WRITE_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
/*
- * For WRITE_STRIP and WRITE_PASS,
- * force the error on data
- * being copied from SLI-Host to SLI-Port.
+ * For WRITE_INSERT, force the
+ * error to be sent on the wire. It should be
+ * detected by the Target.
*/
+ /* DEAD will be the apptag on the wire */
*apptag = 0xDEAD;
phba->lpfc_injerr_wapp_cnt--;
- phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
- rc = BG_ERR_INIT;
+ if (phba->lpfc_injerr_wapp_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_TGT | BG_ERR_CHECK;
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "0812 BLKGRD: Injecting apptag error: "
+ "0813 BLKGRD: Injecting apptag error: "
"write lba x%lx\n", (unsigned long)lba);
break;
- case SCSI_PROT_WRITE_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
/*
- * For WRITE_INSERT, force the
- * error to be sent on the wire. It should be
- * detected by the Target.
+ * For WRITE_STRIP and WRITE_PASS,
+ * force the error on data
+ * being copied from SLI-Host to SLI-Port.
*/
- /* DEAD will be the apptag on the wire */
*apptag = 0xDEAD;
phba->lpfc_injerr_wapp_cnt--;
- phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
- rc = BG_ERR_TGT;
+ if (phba->lpfc_injerr_wapp_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
+ rc = BG_ERR_INIT;
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "0813 BLKGRD: Injecting apptag error: "
+ "0812 BLKGRD: Injecting apptag error: "
"write lba x%lx\n", (unsigned long)lba);
break;
}
@@ -1488,11 +1588,6 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
if (phba->lpfc_injerr_rapp_cnt) {
switch (op) {
case SCSI_PROT_READ_INSERT:
- /*
- * For READ_INSERT, it doesn't make sense
- * to change the apptag.
- */
- break;
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_READ_PASS:
/*
@@ -1502,7 +1597,13 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
*/
*apptag = 0xDEAD;
phba->lpfc_injerr_rapp_cnt--;
- phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+ if (phba->lpfc_injerr_rapp_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
rc = BG_ERR_INIT;
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
@@ -1519,57 +1620,51 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
if (phba->lpfc_injerr_wgrd_cnt) {
switch (op) {
case SCSI_PROT_WRITE_PASS:
- if (blockoff && src) {
- /* Insert error in middle of the IO */
-
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "0815 BLKGRD: Injecting guard error: "
- "write lba x%lx + x%x oldgrdTag x%x\n",
- (unsigned long)lba, blockoff,
- src->guard_tag);
-
- /*
- * NOTE, this will change guard tag in
- * the memory location forever!
- */
- src->guard_tag = 0xDEAD;
- phba->lpfc_injerr_wgrd_cnt--;
- phba->lpfc_injerr_lba =
- LPFC_INJERR_LBA_OFF;
- rc = BG_ERR_CHECK;
- break;
- }
+ rc = BG_ERR_CHECK;
/* Drop thru */
- case SCSI_PROT_WRITE_STRIP:
+
+ case SCSI_PROT_WRITE_INSERT:
/*
- * For WRITE_STRIP and WRITE_PASS,
- * force the error on data
- * being copied from SLI-Host to SLI-Port.
+ * For WRITE_INSERT, force the
+ * error to be sent on the wire. It should be
+ * detected by the Target.
*/
phba->lpfc_injerr_wgrd_cnt--;
- phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+ if (phba->lpfc_injerr_wgrd_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
- rc = BG_ERR_SWAP;
+ rc |= BG_ERR_TGT | BG_ERR_SWAP;
/* Signals the caller to swap CRC->CSUM */
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "0816 BLKGRD: Injecting guard error: "
+ "0817 BLKGRD: Injecting guard error: "
"write lba x%lx\n", (unsigned long)lba);
break;
- case SCSI_PROT_WRITE_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
/*
- * For WRITE_INSERT, force the
- * error to be sent on the wire. It should be
- * detected by the Target.
+ * For WRITE_STRIP and WRITE_PASS,
+ * force the error on data
+ * being copied from SLI-Host to SLI-Port.
*/
phba->lpfc_injerr_wgrd_cnt--;
- phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+ if (phba->lpfc_injerr_wgrd_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
- rc = BG_ERR_SWAP;
+ rc = BG_ERR_INIT | BG_ERR_SWAP;
/* Signals the caller to swap CRC->CSUM */
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "0817 BLKGRD: Injecting guard error: "
+ "0816 BLKGRD: Injecting guard error: "
"write lba x%lx\n", (unsigned long)lba);
break;
}
@@ -1577,11 +1672,6 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
if (phba->lpfc_injerr_rgrd_cnt) {
switch (op) {
case SCSI_PROT_READ_INSERT:
- /*
- * For READ_INSERT, it doesn't make sense
- * to change the guard tag.
- */
- break;
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_READ_PASS:
/*
@@ -1589,11 +1679,16 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
* error on data being read off the wire. It
* should force an IO error to the driver.
*/
- *apptag = 0xDEAD;
phba->lpfc_injerr_rgrd_cnt--;
- phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+ if (phba->lpfc_injerr_rgrd_cnt == 0) {
+ phba->lpfc_injerr_nportid = 0;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ memset(&phba->lpfc_injerr_wwpn,
+ 0, sizeof(struct lpfc_name));
+ }
- rc = BG_ERR_SWAP;
+ rc = BG_ERR_INIT | BG_ERR_SWAP;
/* Signals the caller to swap CRC->CSUM */
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
@@ -1629,20 +1724,20 @@ lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
switch (scsi_get_prot_op(sc)) {
case SCSI_PROT_READ_INSERT:
case SCSI_PROT_WRITE_STRIP:
- *txop = BG_OP_IN_CSUM_OUT_NODIF;
*rxop = BG_OP_IN_NODIF_OUT_CSUM;
+ *txop = BG_OP_IN_CSUM_OUT_NODIF;
break;
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_WRITE_INSERT:
- *txop = BG_OP_IN_NODIF_OUT_CRC;
*rxop = BG_OP_IN_CRC_OUT_NODIF;
+ *txop = BG_OP_IN_NODIF_OUT_CRC;
break;
case SCSI_PROT_READ_PASS:
case SCSI_PROT_WRITE_PASS:
- *txop = BG_OP_IN_CSUM_OUT_CRC;
*rxop = BG_OP_IN_CRC_OUT_CSUM;
+ *txop = BG_OP_IN_CSUM_OUT_CRC;
break;
case SCSI_PROT_NORMAL:
@@ -1658,20 +1753,20 @@ lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
switch (scsi_get_prot_op(sc)) {
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_WRITE_INSERT:
- *txop = BG_OP_IN_NODIF_OUT_CRC;
*rxop = BG_OP_IN_CRC_OUT_NODIF;
+ *txop = BG_OP_IN_NODIF_OUT_CRC;
break;
case SCSI_PROT_READ_PASS:
case SCSI_PROT_WRITE_PASS:
- *txop = BG_OP_IN_CRC_OUT_CRC;
*rxop = BG_OP_IN_CRC_OUT_CRC;
+ *txop = BG_OP_IN_CRC_OUT_CRC;
break;
case SCSI_PROT_READ_INSERT:
case SCSI_PROT_WRITE_STRIP:
- *txop = BG_OP_IN_CRC_OUT_NODIF;
*rxop = BG_OP_IN_NODIF_OUT_CRC;
+ *txop = BG_OP_IN_CRC_OUT_NODIF;
break;
case SCSI_PROT_NORMAL:
@@ -1710,20 +1805,20 @@ lpfc_bg_err_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
switch (scsi_get_prot_op(sc)) {
case SCSI_PROT_READ_INSERT:
case SCSI_PROT_WRITE_STRIP:
- *txop = BG_OP_IN_CRC_OUT_NODIF;
*rxop = BG_OP_IN_NODIF_OUT_CRC;
+ *txop = BG_OP_IN_CRC_OUT_NODIF;
break;
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_WRITE_INSERT:
- *txop = BG_OP_IN_NODIF_OUT_CSUM;
*rxop = BG_OP_IN_CSUM_OUT_NODIF;
+ *txop = BG_OP_IN_NODIF_OUT_CSUM;
break;
case SCSI_PROT_READ_PASS:
case SCSI_PROT_WRITE_PASS:
- *txop = BG_OP_IN_CRC_OUT_CRC;
- *rxop = BG_OP_IN_CRC_OUT_CRC;
+ *rxop = BG_OP_IN_CSUM_OUT_CRC;
+ *txop = BG_OP_IN_CRC_OUT_CSUM;
break;
case SCSI_PROT_NORMAL:
@@ -1735,20 +1830,20 @@ lpfc_bg_err_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
switch (scsi_get_prot_op(sc)) {
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_WRITE_INSERT:
- *txop = BG_OP_IN_NODIF_OUT_CSUM;
*rxop = BG_OP_IN_CSUM_OUT_NODIF;
+ *txop = BG_OP_IN_NODIF_OUT_CSUM;
break;
case SCSI_PROT_READ_PASS:
case SCSI_PROT_WRITE_PASS:
- *txop = BG_OP_IN_CSUM_OUT_CRC;
- *rxop = BG_OP_IN_CRC_OUT_CSUM;
+ *rxop = BG_OP_IN_CSUM_OUT_CSUM;
+ *txop = BG_OP_IN_CSUM_OUT_CSUM;
break;
case SCSI_PROT_READ_INSERT:
case SCSI_PROT_WRITE_STRIP:
- *txop = BG_OP_IN_CSUM_OUT_NODIF;
*rxop = BG_OP_IN_NODIF_OUT_CSUM;
+ *txop = BG_OP_IN_CSUM_OUT_NODIF;
break;
case SCSI_PROT_NORMAL:
@@ -1817,11 +1912,11 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
- rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+ rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1);
if (rc) {
- if (rc == BG_ERR_SWAP)
+ if (rc & BG_ERR_SWAP)
lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
- if (rc == BG_ERR_CHECK)
+ if (rc & BG_ERR_CHECK)
checking = 0;
}
#endif
@@ -1964,11 +2059,11 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
- rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+ rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1);
if (rc) {
- if (rc == BG_ERR_SWAP)
+ if (rc & BG_ERR_SWAP)
lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
- if (rc == BG_ERR_CHECK)
+ if (rc & BG_ERR_CHECK)
checking = 0;
}
#endif
@@ -2172,11 +2267,11 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
- rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+ rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1);
if (rc) {
- if (rc == BG_ERR_SWAP)
+ if (rc & BG_ERR_SWAP)
lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
- if (rc == BG_ERR_CHECK)
+ if (rc & BG_ERR_CHECK)
checking = 0;
}
#endif
@@ -2312,11 +2407,11 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
- rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+ rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1);
if (rc) {
- if (rc == BG_ERR_SWAP)
+ if (rc & BG_ERR_SWAP)
lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
- if (rc == BG_ERR_CHECK)
+ if (rc & BG_ERR_CHECK)
checking = 0;
}
#endif
@@ -2788,7 +2883,7 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
/* No error was reported - problem in FW? */
cmd->result = ScsiResult(DID_ERROR, 0);
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9057 BLKGRD: no errors reported!\n");
+ "9057 BLKGRD: Unknown error reported!\n");
}
out:
@@ -3460,6 +3555,37 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
/* pick up SLI4 exhange busy status from HBA */
lpfc_cmd->exch_busy = pIocbOut->iocb_flag & LPFC_EXCHANGE_BUSY;
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ if (lpfc_cmd->prot_data_type) {
+ struct scsi_dif_tuple *src = NULL;
+
+ src = (struct scsi_dif_tuple *)lpfc_cmd->prot_data_segment;
+ /*
+ * Used to restore any changes to protection
+ * data for error injection.
+ */
+ switch (lpfc_cmd->prot_data_type) {
+ case LPFC_INJERR_REFTAG:
+ src->ref_tag =
+ lpfc_cmd->prot_data;
+ break;
+ case LPFC_INJERR_APPTAG:
+ src->app_tag =
+ (uint16_t)lpfc_cmd->prot_data;
+ break;
+ case LPFC_INJERR_GUARD:
+ src->guard_tag =
+ (uint16_t)lpfc_cmd->prot_data;
+ break;
+ default:
+ break;
+ }
+
+ lpfc_cmd->prot_data = 0;
+ lpfc_cmd->prot_data_type = 0;
+ lpfc_cmd->prot_data_segment = NULL;
+ }
+#endif
if (pnode && NLP_CHK_NODE_ACT(pnode))
atomic_dec(&pnode->cmd_pending);
@@ -4061,15 +4187,6 @@ lpfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
cmnd->result = err;
goto out_fail_command;
}
- /*
- * Do not let the mid-layer retry I/O too fast. If an I/O is retried
- * without waiting a bit then indicate that the device is busy.
- */
- if (cmnd->retries &&
- time_before(jiffies, (cmnd->jiffies_at_alloc +
- msecs_to_jiffies(LPFC_RETRY_PAUSE *
- cmnd->retries))))
- return SCSI_MLQUEUE_DEVICE_BUSY;
ndlp = rdata->pnode;
if ((scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) &&
@@ -4119,63 +4236,48 @@ lpfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
if (vport->phba->cfg_enable_bg) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9033 BLKGRD: rcvd protected cmd:%02x op:%02x "
- "str=%s\n",
- cmnd->cmnd[0], scsi_get_prot_op(cmnd),
- dif_op_str[scsi_get_prot_op(cmnd)]);
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9034 BLKGRD: CDB: %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x\n",
- cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
- cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
- cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
- cmnd->cmnd[9]);
+ "9033 BLKGRD: rcvd protected cmd:%02x op=%s "
+ "guard=%s\n", cmnd->cmnd[0],
+ dif_op_str[scsi_get_prot_op(cmnd)],
+ dif_grd_str[scsi_host_get_guard(shost)]);
if (cmnd->cmnd[0] == READ_10)
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9035 BLKGRD: READ @ sector %llu, "
- "count %u\n",
+ "cnt %u, rpt %d\n",
(unsigned long long)scsi_get_lba(cmnd),
- blk_rq_sectors(cmnd->request));
+ blk_rq_sectors(cmnd->request),
+ (cmnd->cmnd[1]>>5));
else if (cmnd->cmnd[0] == WRITE_10)
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9036 BLKGRD: WRITE @ sector %llu, "
- "count %u cmd=%p\n",
+ "cnt %u, wpt %d\n",
(unsigned long long)scsi_get_lba(cmnd),
blk_rq_sectors(cmnd->request),
- cmnd);
+ (cmnd->cmnd[1]>>5));
}
err = lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
} else {
if (vport->phba->cfg_enable_bg) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9038 BLKGRD: rcvd unprotected cmd:"
- "%02x op:%02x str=%s\n",
- cmnd->cmnd[0], scsi_get_prot_op(cmnd),
- dif_op_str[scsi_get_prot_op(cmnd)]);
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9039 BLKGRD: CDB: %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x\n",
- cmnd->cmnd[0], cmnd->cmnd[1],
- cmnd->cmnd[2], cmnd->cmnd[3],
- cmnd->cmnd[4], cmnd->cmnd[5],
- cmnd->cmnd[6], cmnd->cmnd[7],
- cmnd->cmnd[8], cmnd->cmnd[9]);
+ "9038 BLKGRD: rcvd unprotected cmd:"
+ "%02x op=%s guard=%s\n", cmnd->cmnd[0],
+ dif_op_str[scsi_get_prot_op(cmnd)],
+ dif_grd_str[scsi_host_get_guard(shost)]);
if (cmnd->cmnd[0] == READ_10)
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9040 dbg: READ @ sector %llu, "
- "count %u\n",
+ "cnt %u, rpt %d\n",
(unsigned long long)scsi_get_lba(cmnd),
- blk_rq_sectors(cmnd->request));
+ blk_rq_sectors(cmnd->request),
+ (cmnd->cmnd[1]>>5));
else if (cmnd->cmnd[0] == WRITE_10)
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9041 dbg: WRITE @ sector %llu, "
- "count %u cmd=%p\n",
- (unsigned long long)scsi_get_lba(cmnd),
- blk_rq_sectors(cmnd->request), cmnd);
- else
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9042 dbg: parser not implemented\n");
+ "9041 dbg: WRITE @ sector %llu, "
+ "cnt %u, wpt %d\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ blk_rq_sectors(cmnd->request),
+ (cmnd->cmnd[1]>>5));
}
err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
}
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
index 9075a08..21a2ffe 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.h
+++ b/drivers/scsi/lpfc/lpfc_scsi.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2006 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2012 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -150,9 +150,18 @@ struct lpfc_scsi_buf {
struct lpfc_iocbq cur_iocbq;
wait_queue_head_t *waitq;
unsigned long start_time;
+
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+ /* Used to restore any changes to protection data for error injection */
+ void *prot_data_segment;
+ uint32_t prot_data;
+ uint32_t prot_data_type;
+#define LPFC_INJERR_REFTAG 1
+#define LPFC_INJERR_APPTAG 2
+#define LPFC_INJERR_GUARD 3
+#endif
};
#define LPFC_SCSI_DMA_EXT_SIZE 264
#define LPFC_BPL_SIZE 1024
-#define LPFC_RETRY_PAUSE 300
#define MDAC_DIRECT_CMD 0x22
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index e0e4d8d..dbaf5b9 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2012 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -5578,8 +5578,6 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba)
for (i = 0; i < count; i++)
phba->sli4_hba.rpi_ids[i] = base + i;
- lpfc_sli4_node_prep(phba);
-
/* VPIs. */
count = phba->sli4_hba.max_cfg_param.max_vpi;
base = phba->sli4_hba.max_cfg_param.vpi_base;
@@ -5613,6 +5611,8 @@ lpfc_sli4_alloc_resource_identifiers(struct lpfc_hba *phba)
rc = -ENOMEM;
goto free_vpi_ids;
}
+ phba->sli4_hba.max_cfg_param.xri_used = 0;
+ phba->sli4_hba.xri_count = 0;
phba->sli4_hba.xri_ids = kzalloc(count *
sizeof(uint16_t),
GFP_KERNEL);
@@ -6147,6 +6147,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
rc = -ENODEV;
goto out_free_mbox;
}
+ lpfc_sli4_node_prep(phba);
/* Create all the SLI4 queues */
rc = lpfc_sli4_queue_create(phba);
@@ -7251,11 +7252,13 @@ lpfc_sli4_post_async_mbox(struct lpfc_hba *phba)
out_not_finished:
spin_lock_irqsave(&phba->hbalock, iflags);
- mboxq->u.mb.mbxStatus = MBX_NOT_FINISHED;
- __lpfc_mbox_cmpl_put(phba, mboxq);
- /* Release the token */
- psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
- phba->sli.mbox_active = NULL;
+ if (phba->sli.mbox_active) {
+ mboxq->u.mb.mbxStatus = MBX_NOT_FINISHED;
+ __lpfc_mbox_cmpl_put(phba, mboxq);
+ /* Release the token */
+ psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+ phba->sli.mbox_active = NULL;
+ }
spin_unlock_irqrestore(&phba->hbalock, iflags);
return MBX_NOT_FINISHED;
@@ -7743,6 +7746,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
if (pcmd && (*pcmd == ELS_CMD_FLOGI ||
*pcmd == ELS_CMD_SCR ||
*pcmd == ELS_CMD_FDISC ||
+ *pcmd == ELS_CMD_LOGO ||
*pcmd == ELS_CMD_PLOGI)) {
bf_set(els_req64_sp, &wqe->els_req, 1);
bf_set(els_req64_sid, &wqe->els_req,
@@ -8385,6 +8389,7 @@ lpfc_sli4_abts_err_handler(struct lpfc_hba *phba,
struct sli4_wcqe_xri_aborted *axri)
{
struct lpfc_vport *vport;
+ uint32_t ext_status = 0;
if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
@@ -8396,12 +8401,20 @@ lpfc_sli4_abts_err_handler(struct lpfc_hba *phba,
vport = ndlp->vport;
lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
"3116 Port generated FCP XRI ABORT event on "
- "vpi %d rpi %d xri x%x status 0x%x\n",
+ "vpi %d rpi %d xri x%x status 0x%x parameter x%x\n",
ndlp->vport->vpi, ndlp->nlp_rpi,
bf_get(lpfc_wcqe_xa_xri, axri),
- bf_get(lpfc_wcqe_xa_status, axri));
+ bf_get(lpfc_wcqe_xa_status, axri),
+ axri->parameter);
- if (bf_get(lpfc_wcqe_xa_status, axri) == IOSTAT_LOCAL_REJECT)
+ /*
+ * Catch the ABTS protocol failure case. Older OCe FW releases returned
+ * LOCAL_REJECT and 0 for a failed ABTS exchange and later OCe and
+ * LPe FW releases returned LOCAL_REJECT and SEQUENCE_TIMEOUT.
+ */
+ ext_status = axri->parameter & WCQE_PARAM_MASK;
+ if ((bf_get(lpfc_wcqe_xa_status, axri) == IOSTAT_LOCAL_REJECT) &&
+ ((ext_status == IOERR_SEQUENCE_TIMEOUT) || (ext_status == 0)))
lpfc_sli_abts_recover_port(vport, ndlp);
}
@@ -9807,12 +9820,11 @@ lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *phba)
unsigned long timeout;
timeout = msecs_to_jiffies(LPFC_MBOX_TMO * 1000) + jiffies;
+
spin_lock_irq(&phba->hbalock);
psli->sli_flag |= LPFC_SLI_ASYNC_MBX_BLK;
- spin_unlock_irq(&phba->hbalock);
if (psli->sli_flag & LPFC_SLI_ACTIVE) {
- spin_lock_irq(&phba->hbalock);
/* Determine how long we might wait for the active mailbox
* command to be gracefully completed by firmware.
*/
@@ -9831,7 +9843,9 @@ lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *phba)
*/
break;
}
- }
+ } else
+ spin_unlock_irq(&phba->hbalock);
+
lpfc_sli_mbox_sys_flush(phba);
}
@@ -13272,7 +13286,7 @@ lpfc_sli4_post_els_sgl_list_ext(struct lpfc_hba *phba)
LPFC_MBOXQ_t *mbox;
uint32_t reqlen, alloclen, index;
uint32_t mbox_tmo;
- uint16_t rsrc_start, rsrc_size, els_xri_cnt;
+ uint16_t rsrc_start, rsrc_size, els_xri_cnt, post_els_xri_cnt;
uint16_t xritag_start = 0, lxri = 0;
struct lpfc_rsrc_blks *rsrc_blk;
int cnt, ttl_cnt, rc = 0;
@@ -13294,6 +13308,7 @@ lpfc_sli4_post_els_sgl_list_ext(struct lpfc_hba *phba)
cnt = 0;
ttl_cnt = 0;
+ post_els_xri_cnt = els_xri_cnt;
list_for_each_entry(rsrc_blk, &phba->sli4_hba.lpfc_xri_blk_list,
list) {
rsrc_start = rsrc_blk->rsrc_start;
@@ -13303,11 +13318,12 @@ lpfc_sli4_post_els_sgl_list_ext(struct lpfc_hba *phba)
"3014 Working ELS Extent start %d, cnt %d\n",
rsrc_start, rsrc_size);
- loop_cnt = min(els_xri_cnt, rsrc_size);
- if (ttl_cnt + loop_cnt >= els_xri_cnt) {
- loop_cnt = els_xri_cnt - ttl_cnt;
- ttl_cnt = els_xri_cnt;
- }
+ loop_cnt = min(post_els_xri_cnt, rsrc_size);
+ if (loop_cnt < post_els_xri_cnt) {
+ post_els_xri_cnt -= loop_cnt;
+ ttl_cnt += loop_cnt;
+ } else
+ ttl_cnt += post_els_xri_cnt;
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
@@ -14203,15 +14219,14 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba,
* field and RX_ID from ABTS for RX_ID field.
*/
bf_set(lpfc_abts_orig, &icmd->un.bls_rsp, LPFC_ABTS_UNSOL_RSP);
- bf_set(lpfc_abts_rxid, &icmd->un.bls_rsp, rxid);
} else {
/* ABTS sent by initiator to CT exchange, construction
* of BA_ACC will need to allocate a new XRI as for the
- * XRI_TAG and RX_ID fields.
+ * XRI_TAG field.
*/
bf_set(lpfc_abts_orig, &icmd->un.bls_rsp, LPFC_ABTS_UNSOL_INT);
- bf_set(lpfc_abts_rxid, &icmd->un.bls_rsp, NO_XRI);
}
+ bf_set(lpfc_abts_rxid, &icmd->un.bls_rsp, rxid);
bf_set(lpfc_abts_oxid, &icmd->un.bls_rsp, oxid);
/* Xmit CT abts response on exchange <xid> */
@@ -15042,6 +15057,7 @@ lpfc_sli4_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index)
LPFC_MBOXQ_t *mboxq;
phba->fcoe_eventtag_at_fcf_scan = phba->fcoe_eventtag;
+ phba->fcoe_cvl_eventtag_attn = phba->fcoe_cvl_eventtag;
mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mboxq) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index f2a2602..25cefc2 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2011 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2012 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.3.29"
+#define LPFC_DRIVER_VERSION "8.3.30"
#define LPFC_DRIVER_NAME "lpfc"
#define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp"
#define LPFC_FP_DRIVER_HANDLER_NAME "lpfc:fp"
diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c
index e617337..e5cd8d8 100644
--- a/drivers/scsi/mac53c94.c
+++ b/drivers/scsi/mac53c94.c
@@ -22,7 +22,6 @@
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/prom.h>
-#include <asm/system.h>
#include <asm/pci-bridge.h>
#include <asm/macio.h>
diff --git a/drivers/scsi/mac_scsi.c b/drivers/scsi/mac_scsi.c
index 2bccfbe..24828b5 100644
--- a/drivers/scsi/mac_scsi.c
+++ b/drivers/scsi/mac_scsi.c
@@ -43,7 +43,6 @@
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
index 4944747..e8a04ae 100644
--- a/drivers/scsi/mesh.c
+++ b/drivers/scsi/mesh.c
@@ -33,7 +33,6 @@
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/prom.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <asm/hydra.h>
#include <asm/processor.h>
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
index 5e69f46..8a59a77 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -657,7 +657,7 @@ _base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info)
return;
/* eat the loginfos associated with task aborts */
- if (ioc->ignore_loginfos && (log_info == 30050000 || log_info ==
+ if (ioc->ignore_loginfos && (log_info == 0x30050000 || log_info ==
0x31140000 || log_info == 0x31130000))
return;
@@ -2060,12 +2060,10 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
{
int i = 0;
char desc[16];
- u8 revision;
u32 iounit_pg1_flags;
u32 bios_version;
bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion);
- pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision);
strncpy(desc, ioc->manu_pg0.ChipName, 16);
printk(MPT2SAS_INFO_FMT "%s: FWVersion(%02d.%02d.%02d.%02d), "
"ChipRevision(0x%02x), BiosVersion(%02d.%02d.%02d.%02d)\n",
@@ -2074,7 +2072,7 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
(ioc->facts.FWVersion.Word & 0x00FF0000) >> 16,
(ioc->facts.FWVersion.Word & 0x0000FF00) >> 8,
ioc->facts.FWVersion.Word & 0x000000FF,
- revision,
+ ioc->pdev->revision,
(bios_version & 0xFF000000) >> 24,
(bios_version & 0x00FF0000) >> 16,
(bios_version & 0x0000FF00) >> 8,
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
index 7fceb89..3b9a28e 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
@@ -1026,7 +1026,6 @@ _ctl_getiocinfo(void __user *arg)
{
struct mpt2_ioctl_iocinfo karg;
struct MPT2SAS_ADAPTER *ioc;
- u8 revision;
if (copy_from_user(&karg, arg, sizeof(karg))) {
printk(KERN_ERR "failure at %s:%d/%s()!\n",
@@ -1046,8 +1045,7 @@ _ctl_getiocinfo(void __user *arg)
karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2;
if (ioc->pfacts)
karg.port_number = ioc->pfacts[0].PortNumber;
- pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision);
- karg.hw_rev = revision;
+ karg.hw_rev = ioc->pdev->revision;
karg.pci_id = ioc->pdev->device;
karg.subsystem_device = ioc->pdev->subsystem_device;
karg.subsystem_vendor = ioc->pdev->subsystem_vendor;
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index 4b3b475..5982a58 100644
--- a/drivers/scsi/ncr53c8xx.c
+++ b/drivers/scsi/ncr53c8xx.c
@@ -115,7 +115,6 @@
#include <asm/dma.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c
index 0029249..62b6168 100644
--- a/drivers/scsi/nsp32.c
+++ b/drivers/scsi/nsp32.c
@@ -38,7 +38,6 @@
#include <linux/dma-mapping.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <scsi/scsi.h>
diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c
index de0b1a7..21883a2 100644
--- a/drivers/scsi/osst.c
+++ b/drivers/scsi/osst.c
@@ -54,7 +54,6 @@ static const char * osst_version = "0.99.4";
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <asm/dma.h>
-#include <asm/system.h>
/* The driver prints some debugging information on the console if DEBUG
is defined and non-zero. */
diff --git a/drivers/scsi/pas16.c b/drivers/scsi/pas16.c
index f2018b4..2f72c98 100644
--- a/drivers/scsi/pas16.c
+++ b/drivers/scsi/pas16.c
@@ -113,7 +113,6 @@
#include <linux/module.h>
-#include <asm/system.h>
#include <linux/signal.h>
#include <linux/proc_fs.h>
#include <asm/io.h>
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c
index 3619f6e..9d82ee5 100644
--- a/drivers/scsi/pm8001/pm8001_hwi.c
+++ b/drivers/scsi/pm8001/pm8001_hwi.c
@@ -2093,6 +2093,7 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
struct ata_task_resp *resp ;
u32 *sata_resp;
struct pm8001_device *pm8001_dev;
+ unsigned long flags;
psataPayload = (struct sata_completion_resp *)(piomb + 4);
status = le32_to_cpu(psataPayload->status);
@@ -2382,26 +2383,26 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
ts->stat = SAS_DEV_NO_RESPONSE;
break;
}
- spin_lock_irq(&t->task_state_lock);
+ spin_lock_irqsave(&t->task_state_lock, flags);
t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
t->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
- spin_unlock_irq(&t->task_state_lock);
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("task 0x%p done with io_status 0x%x"
" resp 0x%x stat 0x%x but aborted by upper layer!\n",
t, status, ts->resp, ts->stat));
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
} else if (t->uldd_task) {
- spin_unlock_irq(&t->task_state_lock);
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/* ditto */
spin_unlock_irq(&pm8001_ha->lock);
t->task_done(t);
spin_lock_irq(&pm8001_ha->lock);
} else if (!t->uldd_task) {
- spin_unlock_irq(&t->task_state_lock);
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/*ditto*/
spin_unlock_irq(&pm8001_ha->lock);
@@ -2423,6 +2424,7 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
u32 tag = le32_to_cpu(psataPayload->tag);
u32 port_id = le32_to_cpu(psataPayload->port_id);
u32 dev_id = le32_to_cpu(psataPayload->device_id);
+ unsigned long flags;
ccb = &pm8001_ha->ccb_info[tag];
t = ccb->task;
@@ -2593,26 +2595,26 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
ts->stat = SAS_OPEN_TO;
break;
}
- spin_lock_irq(&t->task_state_lock);
+ spin_lock_irqsave(&t->task_state_lock, flags);
t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
t->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
- spin_unlock_irq(&t->task_state_lock);
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("task 0x%p done with io_status 0x%x"
" resp 0x%x stat 0x%x but aborted by upper layer!\n",
t, event, ts->resp, ts->stat));
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
} else if (t->uldd_task) {
- spin_unlock_irq(&t->task_state_lock);
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/* ditto */
spin_unlock_irq(&pm8001_ha->lock);
t->task_done(t);
spin_lock_irq(&pm8001_ha->lock);
} else if (!t->uldd_task) {
- spin_unlock_irq(&t->task_state_lock);
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/*ditto*/
spin_unlock_irq(&pm8001_ha->lock);
diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
index d838205..6c6486f 100644
--- a/drivers/scsi/qla1280.c
+++ b/drivers/scsi/qla1280.c
@@ -359,7 +359,6 @@
#include <asm/byteorder.h>
#include <asm/processor.h>
#include <asm/types.h>
-#include <asm/system.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c
index 7c9f28b..fc542a9 100644
--- a/drivers/scsi/qla4xxx/ql4_isr.c
+++ b/drivers/scsi/qla4xxx/ql4_isr.c
@@ -431,9 +431,9 @@ static void qla4xxx_mbox_status_entry(struct scsi_qla_host *ha,
mbox_sts_entry->out_mbox[6]));
if (mbox_sts_entry->out_mbox[0] == MBOX_STS_COMMAND_COMPLETE)
- status = QLA_SUCCESS;
+ status = ISCSI_PING_SUCCESS;
else
- status = QLA_ERROR;
+ status = mbox_sts_entry->out_mbox[6];
data_size = sizeof(mbox_sts_entry->out_mbox);
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index 3d94194..ee47820 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -834,7 +834,7 @@ static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc)
static void qla4xxx_set_port_speed(struct Scsi_Host *shost)
{
struct scsi_qla_host *ha = to_qla_host(shost);
- struct iscsi_cls_host *ihost = shost_priv(shost);
+ struct iscsi_cls_host *ihost = shost->shost_data;
uint32_t speed = ISCSI_PORT_SPEED_UNKNOWN;
qla4xxx_get_firmware_state(ha);
@@ -859,7 +859,7 @@ static void qla4xxx_set_port_speed(struct Scsi_Host *shost)
static void qla4xxx_set_port_state(struct Scsi_Host *shost)
{
struct scsi_qla_host *ha = to_qla_host(shost);
- struct iscsi_cls_host *ihost = shost_priv(shost);
+ struct iscsi_cls_host *ihost = shost->shost_data;
uint32_t state = ISCSI_PORT_STATE_DOWN;
if (test_bit(AF_LINK_UP, &ha->flags))
@@ -3445,7 +3445,6 @@ static void qla4xxx_free_adapter(struct scsi_qla_host *ha)
int qla4_8xxx_iospace_config(struct scsi_qla_host *ha)
{
int status = 0;
- uint8_t revision_id;
unsigned long mem_base, mem_len, db_base, db_len;
struct pci_dev *pdev = ha->pdev;
@@ -3457,10 +3456,9 @@ int qla4_8xxx_iospace_config(struct scsi_qla_host *ha)
goto iospace_error_exit;
}
- pci_read_config_byte(pdev, PCI_REVISION_ID, &revision_id);
DEBUG2(printk(KERN_INFO "%s: revision-id=%d\n",
- __func__, revision_id));
- ha->revision_id = revision_id;
+ __func__, pdev->revision));
+ ha->revision_id = pdev->revision;
/* remap phys address */
mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */
diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h
index ede9af9..97b30c1 100644
--- a/drivers/scsi/qla4xxx/ql4_version.h
+++ b/drivers/scsi/qla4xxx/ql4_version.h
@@ -5,4 +5,4 @@
* See LICENSE.qla4xxx for copyright and licensing details.
*/
-#define QLA4XXX_DRIVER_VERSION "5.02.00-k15"
+#define QLA4XXX_DRIVER_VERSION "5.02.00-k16"
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index e40dc1c..b191dd5 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -35,7 +35,6 @@
#include "qlogicpti.h"
#include <asm/dma.h>
-#include <asm/system.h>
#include <asm/ptrace.h>
#include <asm/pgtable.h>
#include <asm/oplib.h>
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 5918561..182d5a5 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -101,6 +101,7 @@ static const char * scsi_debug_version_date = "20100324";
#define DEF_LBPU 0
#define DEF_LBPWS 0
#define DEF_LBPWS10 0
+#define DEF_LBPRZ 1
#define DEF_LOWEST_ALIGNED 0
#define DEF_NO_LUN_0 0
#define DEF_NUM_PARTS 0
@@ -186,6 +187,7 @@ static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
static unsigned int scsi_debug_lbpu = DEF_LBPU;
static unsigned int scsi_debug_lbpws = DEF_LBPWS;
static unsigned int scsi_debug_lbpws10 = DEF_LBPWS10;
+static unsigned int scsi_debug_lbprz = DEF_LBPRZ;
static unsigned int scsi_debug_unmap_alignment = DEF_UNMAP_ALIGNMENT;
static unsigned int scsi_debug_unmap_granularity = DEF_UNMAP_GRANULARITY;
static unsigned int scsi_debug_unmap_max_blocks = DEF_UNMAP_MAX_BLOCKS;
@@ -775,10 +777,10 @@ static int inquiry_evpd_b1(unsigned char *arr)
return 0x3c;
}
-/* Thin provisioning VPD page (SBC-3) */
+/* Logical block provisioning VPD page (SBC-3) */
static int inquiry_evpd_b2(unsigned char *arr)
{
- memset(arr, 0, 0x8);
+ memset(arr, 0, 0x4);
arr[0] = 0; /* threshold exponent */
if (scsi_debug_lbpu)
@@ -790,7 +792,10 @@ static int inquiry_evpd_b2(unsigned char *arr)
if (scsi_debug_lbpws10)
arr[1] |= 1 << 5;
- return 0x8;
+ if (scsi_debug_lbprz)
+ arr[1] |= 1 << 2;
+
+ return 0x4;
}
#define SDEBUG_LONG_INQ_SZ 96
@@ -1071,8 +1076,11 @@ static int resp_readcap16(struct scsi_cmnd * scp,
arr[13] = scsi_debug_physblk_exp & 0xf;
arr[14] = (scsi_debug_lowest_aligned >> 8) & 0x3f;
- if (scsi_debug_lbp())
+ if (scsi_debug_lbp()) {
arr[14] |= 0x80; /* LBPME */
+ if (scsi_debug_lbprz)
+ arr[14] |= 0x40; /* LBPRZ */
+ }
arr[15] = scsi_debug_lowest_aligned & 0xff;
@@ -2046,10 +2054,13 @@ static void unmap_region(sector_t lba, unsigned int len)
block = lba + alignment;
rem = do_div(block, granularity);
- if (rem == 0 && lba + granularity <= end &&
- block < map_size)
+ if (rem == 0 && lba + granularity <= end && block < map_size) {
clear_bit(block, map_storep);
-
+ if (scsi_debug_lbprz)
+ memset(fake_storep +
+ block * scsi_debug_sector_size, 0,
+ scsi_debug_sector_size);
+ }
lba += granularity - rem;
}
}
@@ -2731,6 +2742,7 @@ module_param_named(guard, scsi_debug_guard, int, S_IRUGO);
module_param_named(lbpu, scsi_debug_lbpu, int, S_IRUGO);
module_param_named(lbpws, scsi_debug_lbpws, int, S_IRUGO);
module_param_named(lbpws10, scsi_debug_lbpws10, int, S_IRUGO);
+module_param_named(lbprz, scsi_debug_lbprz, int, S_IRUGO);
module_param_named(lowest_aligned, scsi_debug_lowest_aligned, int, S_IRUGO);
module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR);
module_param_named(max_queue, scsi_debug_max_queue, int, S_IRUGO | S_IWUSR);
@@ -2772,6 +2784,7 @@ MODULE_PARM_DESC(guard, "protection checksum: 0=crc, 1=ip (def=0)");
MODULE_PARM_DESC(lbpu, "enable LBP, support UNMAP command (def=0)");
MODULE_PARM_DESC(lbpws, "enable LBP, support WRITE SAME(16) with UNMAP bit (def=0)");
MODULE_PARM_DESC(lbpws10, "enable LBP, support WRITE SAME(10) with UNMAP bit (def=0)");
+MODULE_PARM_DESC(lbprz, "unmapped blocks return 0 on read (def=1)");
MODULE_PARM_DESC(lowest_aligned, "lowest aligned lba (def=0)");
MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)");
MODULE_PARM_DESC(max_queue, "max number of queued commands (1 to 255(def))");
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index fac3173..1cf640e 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -1486,7 +1486,7 @@ void iscsi_post_host_event(uint32_t host_no, struct iscsi_transport *transport,
struct iscsi_uevent *ev;
int len = NLMSG_SPACE(sizeof(*ev) + data_size);
- skb = alloc_skb(len, GFP_KERNEL);
+ skb = alloc_skb(len, GFP_NOIO);
if (!skb) {
printk(KERN_ERR "gracefully ignored host event (%d):%d OOM\n",
host_no, code);
@@ -1504,7 +1504,7 @@ void iscsi_post_host_event(uint32_t host_no, struct iscsi_transport *transport,
if (data_size)
memcpy((char *)ev + sizeof(*ev), data, data_size);
- iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
+ iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_NOIO);
}
EXPORT_SYMBOL_GPL(iscsi_post_host_event);
@@ -1517,7 +1517,7 @@ void iscsi_ping_comp_event(uint32_t host_no, struct iscsi_transport *transport,
struct iscsi_uevent *ev;
int len = NLMSG_SPACE(sizeof(*ev) + data_size);
- skb = alloc_skb(len, GFP_KERNEL);
+ skb = alloc_skb(len, GFP_NOIO);
if (!skb) {
printk(KERN_ERR "gracefully ignored ping comp: OOM\n");
return;
@@ -1533,7 +1533,7 @@ void iscsi_ping_comp_event(uint32_t host_no, struct iscsi_transport *transport,
ev->r.ping_comp.data_size = data_size;
memcpy((char *)ev + sizeof(*ev), data, data_size);
- iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
+ iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_NOIO);
}
EXPORT_SYMBOL_GPL(iscsi_ping_comp_event);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 09e3df4..5ba5c2a 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -664,7 +664,7 @@ static void sd_unprep_fn(struct request_queue *q, struct request *rq)
}
/**
- * sd_init_command - build a scsi (read or write) command from
+ * sd_prep_fn - build a scsi (read or write) command from
* information in the request structure.
* @SCpnt: pointer to mid-level's per scsi command structure that
* contains request and into which the scsi command is written
@@ -711,7 +711,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
ret = BLKPREP_KILL;
SCSI_LOG_HLQUEUE(1, scmd_printk(KERN_INFO, SCpnt,
- "sd_init_command: block=%llu, "
+ "sd_prep_fn: block=%llu, "
"count=%d\n",
(unsigned long long)block,
this_count));
@@ -1212,9 +1212,14 @@ static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing)
retval = -ENODEV;
if (scsi_block_when_processing_errors(sdp)) {
+ retval = scsi_autopm_get_device(sdp);
+ if (retval)
+ goto out;
+
sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES,
sshdr);
+ scsi_autopm_put_device(sdp);
}
/* failed to execute TUR, assume media not present */
@@ -2644,8 +2649,8 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
* (e.g. /dev/sda). More precisely it is the block device major
* and minor number that is chosen here.
*
- * Assume sd_attach is not re-entrant (for time being)
- * Also think about sd_attach() and sd_remove() running coincidentally.
+ * Assume sd_probe is not re-entrant (for time being)
+ * Also think about sd_probe() and sd_remove() running coincidentally.
**/
static int sd_probe(struct device *dev)
{
@@ -2660,7 +2665,7 @@ static int sd_probe(struct device *dev)
goto out;
SCSI_LOG_HLQUEUE(3, sdev_printk(KERN_INFO, sdp,
- "sd_attach\n"));
+ "sd_probe\n"));
error = -ENOMEM;
sdkp = kzalloc(sizeof(*sdkp), GFP_KERNEL);
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 9262cdf..e41998c 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -42,7 +42,6 @@ static const char *verstr = "20101219";
#include <asm/uaccess.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <scsi/scsi.h>
#include <scsi/scsi_dbg.h>
@@ -1106,6 +1105,12 @@ static int check_tape(struct scsi_tape *STp, struct file *filp)
STp->drv_buffer));
}
STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
+ if (!STp->drv_buffer && STp->immediate_filemark) {
+ printk(KERN_WARNING
+ "%s: non-buffered tape: disabling writing immediate filemarks\n",
+ name);
+ STp->immediate_filemark = 0;
+ }
}
st_release_request(SRpnt);
SRpnt = NULL;
@@ -1314,6 +1319,8 @@ static int st_flush(struct file *filp, fl_owner_t id)
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = WRITE_FILEMARKS;
+ if (STp->immediate_filemark)
+ cmd[1] = 1;
cmd[4] = 1 + STp->two_fm;
SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
@@ -2181,8 +2188,9 @@ static void st_log_options(struct scsi_tape * STp, struct st_modedef * STm, char
name, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
STp->scsi2_logical);
printk(KERN_INFO
- "%s: sysv: %d nowait: %d sili: %d\n", name, STm->sysv, STp->immediate,
- STp->sili);
+ "%s: sysv: %d nowait: %d sili: %d nowait_filemark: %d\n",
+ name, STm->sysv, STp->immediate, STp->sili,
+ STp->immediate_filemark);
printk(KERN_INFO "%s: debugging: %d\n",
name, debugging);
}
@@ -2224,6 +2232,7 @@ static int st_set_options(struct scsi_tape *STp, long options)
STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0;
STp->immediate = (options & MT_ST_NOWAIT) != 0;
+ STp->immediate_filemark = (options & MT_ST_NOWAIT_EOF) != 0;
STm->sysv = (options & MT_ST_SYSV) != 0;
STp->sili = (options & MT_ST_SILI) != 0;
DEB( debugging = (options & MT_ST_DEBUGGING) != 0;
@@ -2255,6 +2264,8 @@ static int st_set_options(struct scsi_tape *STp, long options)
STp->scsi2_logical = value;
if ((options & MT_ST_NOWAIT) != 0)
STp->immediate = value;
+ if ((options & MT_ST_NOWAIT_EOF) != 0)
+ STp->immediate_filemark = value;
if ((options & MT_ST_SYSV) != 0)
STm->sysv = value;
if ((options & MT_ST_SILI) != 0)
@@ -2714,7 +2725,8 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
cmd[0] = WRITE_FILEMARKS;
if (cmd_in == MTWSM)
cmd[1] = 2;
- if (cmd_in == MTWEOFI)
+ if (cmd_in == MTWEOFI ||
+ (cmd_in == MTWEOF && STp->immediate_filemark))
cmd[1] |= 1;
cmd[2] = (arg >> 16);
cmd[3] = (arg >> 8);
@@ -4093,6 +4105,7 @@ static int st_probe(struct device *dev)
tpnt->scsi2_logical = ST_SCSI2LOGICAL;
tpnt->sili = ST_SILI;
tpnt->immediate = ST_NOWAIT;
+ tpnt->immediate_filemark = 0;
tpnt->default_drvbuffer = 0xff; /* No forced buffering */
tpnt->partition = 0;
tpnt->new_partition = 0;
@@ -4478,6 +4491,7 @@ st_options_show(struct device *dev, struct device_attribute *attr, char *buf)
options |= STp->scsi2_logical ? MT_ST_SCSI2LOGICAL : 0;
options |= STm->sysv ? MT_ST_SYSV : 0;
options |= STp->immediate ? MT_ST_NOWAIT : 0;
+ options |= STp->immediate_filemark ? MT_ST_NOWAIT_EOF : 0;
options |= STp->sili ? MT_ST_SILI : 0;
l = snprintf(buf, PAGE_SIZE, "0x%08x\n", options);
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h
index f91a67c..ea35632 100644
--- a/drivers/scsi/st.h
+++ b/drivers/scsi/st.h
@@ -120,6 +120,7 @@ struct scsi_tape {
unsigned char c_algo; /* compression algorithm */
unsigned char pos_unknown; /* after reset position unknown */
unsigned char sili; /* use SILI when reading in variable b mode */
+ unsigned char immediate_filemark; /* write filemark immediately */
int tape_type;
int long_timeout; /* timeout for commands known to take long time */
diff --git a/drivers/scsi/sun3_scsi.c b/drivers/scsi/sun3_scsi.c
index baf7328..6e25889 100644
--- a/drivers/scsi/sun3_scsi.c
+++ b/drivers/scsi/sun3_scsi.c
@@ -63,7 +63,6 @@
#include <linux/blkdev.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sun3ints.h>
#include <asm/dvma.h>
diff --git a/drivers/scsi/sun3_scsi_vme.c b/drivers/scsi/sun3_scsi_vme.c
index fbba78e..a3dd55d 100644
--- a/drivers/scsi/sun3_scsi_vme.c
+++ b/drivers/scsi/sun3_scsi_vme.c
@@ -25,7 +25,6 @@
#include <linux/blkdev.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/sun3ints.h>
#include <asm/dvma.h>
diff --git a/drivers/scsi/sym53c416.c b/drivers/scsi/sym53c416.c
index 012c86e..ac4eca6 100644
--- a/drivers/scsi/sym53c416.c
+++ b/drivers/scsi/sym53c416.c
@@ -37,7 +37,6 @@
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <linux/blkdev.h>
#include <linux/isapnp.h>
diff --git a/drivers/scsi/t128.c b/drivers/scsi/t128.c
index 041eaaa..d672d97 100644
--- a/drivers/scsi/t128.c
+++ b/drivers/scsi/t128.c
@@ -106,7 +106,6 @@
* $Log: t128.c,v $
*/
-#include <asm/system.h>
#include <linux/signal.h>
#include <linux/io.h>
#include <linux/blkdev.h>
diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c
index 90e104d..9c216e5 100644
--- a/drivers/scsi/u14-34f.c
+++ b/drivers/scsi/u14-34f.c
@@ -410,7 +410,6 @@
#include <linux/ioport.h>
#include <linux/delay.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
#include <linux/proc_fs.h>
#include <linux/blkdev.h>
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
new file mode 100644
index 0000000..8f27f9d
--- /dev/null
+++ b/drivers/scsi/ufs/Kconfig
@@ -0,0 +1,49 @@
+#
+# Kernel configuration file for the UFS Host Controller
+#
+# This code is based on drivers/scsi/ufs/Kconfig
+# Copyright (C) 2011 Samsung Samsung India Software Operations
+#
+# Santosh Yaraganavi <santosh.sy@samsung.com>
+# Vinayak Holikatti <h.vinayak@samsung.com>
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# NO WARRANTY
+# THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+# LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+# solely responsible for determining the appropriateness of using and
+# distributing the Program and assumes all risks associated with its
+# exercise of rights under this Agreement, including but not limited to
+# the risks and costs of program errors, damage to or loss of data,
+# programs or equipment, and unavailability or interruption of operations.
+
+# DISCLAIMER OF LIABILITY
+# NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+# HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+config SCSI_UFSHCD
+ tristate "Universal Flash Storage host controller driver"
+ depends on PCI && SCSI
+ ---help---
+ This is a generic driver which supports PCIe UFS Host controllers.
diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
new file mode 100644
index 0000000..adf7895
--- /dev/null
+++ b/drivers/scsi/ufs/Makefile
@@ -0,0 +1,2 @@
+# UFSHCD makefile
+obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
new file mode 100644
index 0000000..b207529
--- /dev/null
+++ b/drivers/scsi/ufs/ufs.h
@@ -0,0 +1,207 @@
+/*
+ * Universal Flash Storage Host controller driver
+ *
+ * This code is based on drivers/scsi/ufs/ufs.h
+ * Copyright (C) 2011-2012 Samsung India Software Operations
+ *
+ * Santosh Yaraganavi <santosh.sy@samsung.com>
+ * Vinayak Holikatti <h.vinayak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef _UFS_H
+#define _UFS_H
+
+#define MAX_CDB_SIZE 16
+
+#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
+ ((byte3 << 24) | (byte2 << 16) |\
+ (byte1 << 8) | (byte0))
+
+/*
+ * UFS Protocol Information Unit related definitions
+ */
+
+/* Task management functions */
+enum {
+ UFS_ABORT_TASK = 0x01,
+ UFS_ABORT_TASK_SET = 0x02,
+ UFS_CLEAR_TASK_SET = 0x04,
+ UFS_LOGICAL_RESET = 0x08,
+ UFS_QUERY_TASK = 0x80,
+ UFS_QUERY_TASK_SET = 0x81,
+};
+
+/* UTP UPIU Transaction Codes Initiator to Target */
+enum {
+ UPIU_TRANSACTION_NOP_OUT = 0x00,
+ UPIU_TRANSACTION_COMMAND = 0x01,
+ UPIU_TRANSACTION_DATA_OUT = 0x02,
+ UPIU_TRANSACTION_TASK_REQ = 0x04,
+ UPIU_TRANSACTION_QUERY_REQ = 0x26,
+};
+
+/* UTP UPIU Transaction Codes Target to Initiator */
+enum {
+ UPIU_TRANSACTION_NOP_IN = 0x20,
+ UPIU_TRANSACTION_RESPONSE = 0x21,
+ UPIU_TRANSACTION_DATA_IN = 0x22,
+ UPIU_TRANSACTION_TASK_RSP = 0x24,
+ UPIU_TRANSACTION_READY_XFER = 0x31,
+ UPIU_TRANSACTION_QUERY_RSP = 0x36,
+};
+
+/* UPIU Read/Write flags */
+enum {
+ UPIU_CMD_FLAGS_NONE = 0x00,
+ UPIU_CMD_FLAGS_WRITE = 0x20,
+ UPIU_CMD_FLAGS_READ = 0x40,
+};
+
+/* UPIU Task Attributes */
+enum {
+ UPIU_TASK_ATTR_SIMPLE = 0x00,
+ UPIU_TASK_ATTR_ORDERED = 0x01,
+ UPIU_TASK_ATTR_HEADQ = 0x02,
+ UPIU_TASK_ATTR_ACA = 0x03,
+};
+
+/* UTP QUERY Transaction Specific Fields OpCode */
+enum {
+ UPIU_QUERY_OPCODE_NOP = 0x0,
+ UPIU_QUERY_OPCODE_READ_DESC = 0x1,
+ UPIU_QUERY_OPCODE_WRITE_DESC = 0x2,
+ UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
+ UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4,
+ UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
+ UPIU_QUERY_OPCODE_SET_FLAG = 0x6,
+ UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7,
+ UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
+};
+
+/* UTP Transfer Request Command Type (CT) */
+enum {
+ UPIU_COMMAND_SET_TYPE_SCSI = 0x0,
+ UPIU_COMMAND_SET_TYPE_UFS = 0x1,
+ UPIU_COMMAND_SET_TYPE_QUERY = 0x2,
+};
+
+enum {
+ MASK_SCSI_STATUS = 0xFF,
+ MASK_TASK_RESPONSE = 0xFF00,
+ MASK_RSP_UPIU_RESULT = 0xFFFF,
+};
+
+/* Task management service response */
+enum {
+ UPIU_TASK_MANAGEMENT_FUNC_COMPL = 0x00,
+ UPIU_TASK_MANAGEMENT_FUNC_NOT_SUPPORTED = 0x04,
+ UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED = 0x08,
+ UPIU_TASK_MANAGEMENT_FUNC_FAILED = 0x05,
+ UPIU_INCORRECT_LOGICAL_UNIT_NO = 0x09,
+};
+/**
+ * struct utp_upiu_header - UPIU header structure
+ * @dword_0: UPIU header DW-0
+ * @dword_1: UPIU header DW-1
+ * @dword_2: UPIU header DW-2
+ */
+struct utp_upiu_header {
+ u32 dword_0;
+ u32 dword_1;
+ u32 dword_2;
+};
+
+/**
+ * struct utp_upiu_cmd - Command UPIU structure
+ * @header: UPIU header structure DW-0 to DW-2
+ * @data_transfer_len: Data Transfer Length DW-3
+ * @cdb: Command Descriptor Block CDB DW-4 to DW-7
+ */
+struct utp_upiu_cmd {
+ struct utp_upiu_header header;
+ u32 exp_data_transfer_len;
+ u8 cdb[MAX_CDB_SIZE];
+};
+
+/**
+ * struct utp_upiu_rsp - Response UPIU structure
+ * @header: UPIU header DW-0 to DW-2
+ * @residual_transfer_count: Residual transfer count DW-3
+ * @reserved: Reserved double words DW-4 to DW-7
+ * @sense_data_len: Sense data length DW-8 U16
+ * @sense_data: Sense data field DW-8 to DW-12
+ */
+struct utp_upiu_rsp {
+ struct utp_upiu_header header;
+ u32 residual_transfer_count;
+ u32 reserved[4];
+ u16 sense_data_len;
+ u8 sense_data[18];
+};
+
+/**
+ * struct utp_upiu_task_req - Task request UPIU structure
+ * @header - UPIU header structure DW0 to DW-2
+ * @input_param1: Input parameter 1 DW-3
+ * @input_param2: Input parameter 2 DW-4
+ * @input_param3: Input parameter 3 DW-5
+ * @reserved: Reserved double words DW-6 to DW-7
+ */
+struct utp_upiu_task_req {
+ struct utp_upiu_header header;
+ u32 input_param1;
+ u32 input_param2;
+ u32 input_param3;
+ u32 reserved[2];
+};
+
+/**
+ * struct utp_upiu_task_rsp - Task Management Response UPIU structure
+ * @header: UPIU header structure DW0-DW-2
+ * @output_param1: Ouput parameter 1 DW3
+ * @output_param2: Output parameter 2 DW4
+ * @reserved: Reserved double words DW-5 to DW-7
+ */
+struct utp_upiu_task_rsp {
+ struct utp_upiu_header header;
+ u32 output_param1;
+ u32 output_param2;
+ u32 reserved[3];
+};
+
+#endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
new file mode 100644
index 0000000..52b96e8
--- /dev/null
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -0,0 +1,1978 @@
+/*
+ * Universal Flash Storage Host controller driver
+ *
+ * This code is based on drivers/scsi/ufs/ufshcd.c
+ * Copyright (C) 2011-2012 Samsung India Software Operations
+ *
+ * Santosh Yaraganavi <santosh.sy@samsung.com>
+ * Vinayak Holikatti <h.vinayak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
+
+#include "ufs.h"
+#include "ufshci.h"
+
+#define UFSHCD "ufshcd"
+#define UFSHCD_DRIVER_VERSION "0.1"
+
+enum {
+ UFSHCD_MAX_CHANNEL = 0,
+ UFSHCD_MAX_ID = 1,
+ UFSHCD_MAX_LUNS = 8,
+ UFSHCD_CMD_PER_LUN = 32,
+ UFSHCD_CAN_QUEUE = 32,
+};
+
+/* UFSHCD states */
+enum {
+ UFSHCD_STATE_OPERATIONAL,
+ UFSHCD_STATE_RESET,
+ UFSHCD_STATE_ERROR,
+};
+
+/* Interrupt configuration options */
+enum {
+ UFSHCD_INT_DISABLE,
+ UFSHCD_INT_ENABLE,
+ UFSHCD_INT_CLEAR,
+};
+
+/* Interrupt aggregation options */
+enum {
+ INT_AGGR_RESET,
+ INT_AGGR_CONFIG,
+};
+
+/**
+ * struct uic_command - UIC command structure
+ * @command: UIC command
+ * @argument1: UIC command argument 1
+ * @argument2: UIC command argument 2
+ * @argument3: UIC command argument 3
+ * @cmd_active: Indicate if UIC command is outstanding
+ * @result: UIC command result
+ */
+struct uic_command {
+ u32 command;
+ u32 argument1;
+ u32 argument2;
+ u32 argument3;
+ int cmd_active;
+ int result;
+};
+
+/**
+ * struct ufs_hba - per adapter private structure
+ * @mmio_base: UFSHCI base register address
+ * @ucdl_base_addr: UFS Command Descriptor base address
+ * @utrdl_base_addr: UTP Transfer Request Descriptor base address
+ * @utmrdl_base_addr: UTP Task Management Descriptor base address
+ * @ucdl_dma_addr: UFS Command Descriptor DMA address
+ * @utrdl_dma_addr: UTRDL DMA address
+ * @utmrdl_dma_addr: UTMRDL DMA address
+ * @host: Scsi_Host instance of the driver
+ * @pdev: PCI device handle
+ * @lrb: local reference block
+ * @outstanding_tasks: Bits representing outstanding task requests
+ * @outstanding_reqs: Bits representing outstanding transfer requests
+ * @capabilities: UFS Controller Capabilities
+ * @nutrs: Transfer Request Queue depth supported by controller
+ * @nutmrs: Task Management Queue depth supported by controller
+ * @active_uic_cmd: handle of active UIC command
+ * @ufshcd_tm_wait_queue: wait queue for task management
+ * @tm_condition: condition variable for task management
+ * @ufshcd_state: UFSHCD states
+ * @int_enable_mask: Interrupt Mask Bits
+ * @uic_workq: Work queue for UIC completion handling
+ * @feh_workq: Work queue for fatal controller error handling
+ * @errors: HBA errors
+ */
+struct ufs_hba {
+ void __iomem *mmio_base;
+
+ /* Virtual memory reference */
+ struct utp_transfer_cmd_desc *ucdl_base_addr;
+ struct utp_transfer_req_desc *utrdl_base_addr;
+ struct utp_task_req_desc *utmrdl_base_addr;
+
+ /* DMA memory reference */
+ dma_addr_t ucdl_dma_addr;
+ dma_addr_t utrdl_dma_addr;
+ dma_addr_t utmrdl_dma_addr;
+
+ struct Scsi_Host *host;
+ struct pci_dev *pdev;
+
+ struct ufshcd_lrb *lrb;
+
+ unsigned long outstanding_tasks;
+ unsigned long outstanding_reqs;
+
+ u32 capabilities;
+ int nutrs;
+ int nutmrs;
+ u32 ufs_version;
+
+ struct uic_command active_uic_cmd;
+ wait_queue_head_t ufshcd_tm_wait_queue;
+ unsigned long tm_condition;
+
+ u32 ufshcd_state;
+ u32 int_enable_mask;
+
+ /* Work Queues */
+ struct work_struct uic_workq;
+ struct work_struct feh_workq;
+
+ /* HBA Errors */
+ u32 errors;
+};
+
+/**
+ * struct ufshcd_lrb - local reference block
+ * @utr_descriptor_ptr: UTRD address of the command
+ * @ucd_cmd_ptr: UCD address of the command
+ * @ucd_rsp_ptr: Response UPIU address for this command
+ * @ucd_prdt_ptr: PRDT address of the command
+ * @cmd: pointer to SCSI command
+ * @sense_buffer: pointer to sense buffer address of the SCSI command
+ * @sense_bufflen: Length of the sense buffer
+ * @scsi_status: SCSI status of the command
+ * @command_type: SCSI, UFS, Query.
+ * @task_tag: Task tag of the command
+ * @lun: LUN of the command
+ */
+struct ufshcd_lrb {
+ struct utp_transfer_req_desc *utr_descriptor_ptr;
+ struct utp_upiu_cmd *ucd_cmd_ptr;
+ struct utp_upiu_rsp *ucd_rsp_ptr;
+ struct ufshcd_sg_entry *ucd_prdt_ptr;
+
+ struct scsi_cmnd *cmd;
+ u8 *sense_buffer;
+ unsigned int sense_bufflen;
+ int scsi_status;
+
+ int command_type;
+ int task_tag;
+ unsigned int lun;
+};
+
+/**
+ * ufshcd_get_ufs_version - Get the UFS version supported by the HBA
+ * @hba - Pointer to adapter instance
+ *
+ * Returns UFSHCI version supported by the controller
+ */
+static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba)
+{
+ return readl(hba->mmio_base + REG_UFS_VERSION);
+}
+
+/**
+ * ufshcd_is_device_present - Check if any device connected to
+ * the host controller
+ * @reg_hcs - host controller status register value
+ *
+ * Returns 0 if device present, non-zero if no device detected
+ */
+static inline int ufshcd_is_device_present(u32 reg_hcs)
+{
+ return (DEVICE_PRESENT & reg_hcs) ? 0 : -1;
+}
+
+/**
+ * ufshcd_get_tr_ocs - Get the UTRD Overall Command Status
+ * @lrb: pointer to local command reference block
+ *
+ * This function is used to get the OCS field from UTRD
+ * Returns the OCS field in the UTRD
+ */
+static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
+{
+ return lrbp->utr_descriptor_ptr->header.dword_2 & MASK_OCS;
+}
+
+/**
+ * ufshcd_get_tmr_ocs - Get the UTMRD Overall Command Status
+ * @task_req_descp: pointer to utp_task_req_desc structure
+ *
+ * This function is used to get the OCS field from UTMRD
+ * Returns the OCS field in the UTMRD
+ */
+static inline int
+ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp)
+{
+ return task_req_descp->header.dword_2 & MASK_OCS;
+}
+
+/**
+ * ufshcd_get_tm_free_slot - get a free slot for task management request
+ * @hba: per adapter instance
+ *
+ * Returns maximum number of task management request slots in case of
+ * task management queue full or returns the free slot number
+ */
+static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba)
+{
+ return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs);
+}
+
+/**
+ * ufshcd_utrl_clear - Clear a bit in UTRLCLR register
+ * @hba: per adapter instance
+ * @pos: position of the bit to be cleared
+ */
+static inline void ufshcd_utrl_clear(struct ufs_hba *hba, u32 pos)
+{
+ writel(~(1 << pos),
+ (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_CLEAR));
+}
+
+/**
+ * ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
+ * @reg: Register value of host controller status
+ *
+ * Returns integer, 0 on Success and positive value if failed
+ */
+static inline int ufshcd_get_lists_status(u32 reg)
+{
+ /*
+ * The mask 0xFF is for the following HCS register bits
+ * Bit Description
+ * 0 Device Present
+ * 1 UTRLRDY
+ * 2 UTMRLRDY
+ * 3 UCRDY
+ * 4 HEI
+ * 5 DEI
+ * 6-7 reserved
+ */
+ return (((reg) & (0xFF)) >> 1) ^ (0x07);
+}
+
+/**
+ * ufshcd_get_uic_cmd_result - Get the UIC command result
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the result of UIC command completion
+ * Returns 0 on success, non zero value on error
+ */
+static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
+{
+ return readl(hba->mmio_base + REG_UIC_COMMAND_ARG_2) &
+ MASK_UIC_COMMAND_RESULT;
+}
+
+/**
+ * ufshcd_free_hba_memory - Free allocated memory for LRB, request
+ * and task lists
+ * @hba: Pointer to adapter instance
+ */
+static inline void ufshcd_free_hba_memory(struct ufs_hba *hba)
+{
+ size_t utmrdl_size, utrdl_size, ucdl_size;
+
+ kfree(hba->lrb);
+
+ if (hba->utmrdl_base_addr) {
+ utmrdl_size = sizeof(struct utp_task_req_desc) * hba->nutmrs;
+ dma_free_coherent(&hba->pdev->dev, utmrdl_size,
+ hba->utmrdl_base_addr, hba->utmrdl_dma_addr);
+ }
+
+ if (hba->utrdl_base_addr) {
+ utrdl_size =
+ (sizeof(struct utp_transfer_req_desc) * hba->nutrs);
+ dma_free_coherent(&hba->pdev->dev, utrdl_size,
+ hba->utrdl_base_addr, hba->utrdl_dma_addr);
+ }
+
+ if (hba->ucdl_base_addr) {
+ ucdl_size =
+ (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs);
+ dma_free_coherent(&hba->pdev->dev, ucdl_size,
+ hba->ucdl_base_addr, hba->ucdl_dma_addr);
+ }
+}
+
+/**
+ * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * This function checks the response UPIU for valid transaction type in
+ * response field
+ * Returns 0 on success, non-zero on failure
+ */
+static inline int
+ufshcd_is_valid_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+ return ((be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24) ==
+ UPIU_TRANSACTION_RESPONSE) ? 0 : DID_ERROR << 16;
+}
+
+/**
+ * ufshcd_get_rsp_upiu_result - Get the result from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * This function gets the response status and scsi_status from response UPIU
+ * Returns the response result code.
+ */
+static inline int
+ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+ return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
+}
+
+/**
+ * ufshcd_config_int_aggr - Configure interrupt aggregation values.
+ * Currently there is no use case where we want to configure
+ * interrupt aggregation dynamically. So to configure interrupt
+ * aggregation, #define INT_AGGR_COUNTER_THRESHOLD_VALUE and
+ * INT_AGGR_TIMEOUT_VALUE are used.
+ * @hba: per adapter instance
+ * @option: Interrupt aggregation option
+ */
+static inline void
+ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
+{
+ switch (option) {
+ case INT_AGGR_RESET:
+ writel((INT_AGGR_ENABLE |
+ INT_AGGR_COUNTER_AND_TIMER_RESET),
+ (hba->mmio_base +
+ REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
+ break;
+ case INT_AGGR_CONFIG:
+ writel((INT_AGGR_ENABLE |
+ INT_AGGR_PARAM_WRITE |
+ INT_AGGR_COUNTER_THRESHOLD_VALUE |
+ INT_AGGR_TIMEOUT_VALUE),
+ (hba->mmio_base +
+ REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
+ break;
+ }
+}
+
+/**
+ * ufshcd_enable_run_stop_reg - Enable run-stop registers,
+ * When run-stop registers are set to 1, it indicates the
+ * host controller that it can process the requests
+ * @hba: per adapter instance
+ */
+static void ufshcd_enable_run_stop_reg(struct ufs_hba *hba)
+{
+ writel(UTP_TASK_REQ_LIST_RUN_STOP_BIT,
+ (hba->mmio_base +
+ REG_UTP_TASK_REQ_LIST_RUN_STOP));
+ writel(UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT,
+ (hba->mmio_base +
+ REG_UTP_TRANSFER_REQ_LIST_RUN_STOP));
+}
+
+/**
+ * ufshcd_hba_stop - Send controller to reset state
+ * @hba: per adapter instance
+ */
+static inline void ufshcd_hba_stop(struct ufs_hba *hba)
+{
+ writel(CONTROLLER_DISABLE, (hba->mmio_base + REG_CONTROLLER_ENABLE));
+}
+
+/**
+ * ufshcd_hba_start - Start controller initialization sequence
+ * @hba: per adapter instance
+ */
+static inline void ufshcd_hba_start(struct ufs_hba *hba)
+{
+ writel(CONTROLLER_ENABLE , (hba->mmio_base + REG_CONTROLLER_ENABLE));
+}
+
+/**
+ * ufshcd_is_hba_active - Get controller state
+ * @hba: per adapter instance
+ *
+ * Returns zero if controller is active, 1 otherwise
+ */
+static inline int ufshcd_is_hba_active(struct ufs_hba *hba)
+{
+ return (readl(hba->mmio_base + REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
+}
+
+/**
+ * ufshcd_send_command - Send SCSI or device management commands
+ * @hba: per adapter instance
+ * @task_tag: Task tag of the command
+ */
+static inline
+void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
+{
+ __set_bit(task_tag, &hba->outstanding_reqs);
+ writel((1 << task_tag),
+ (hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL));
+}
+
+/**
+ * ufshcd_copy_sense_data - Copy sense data in case of check condition
+ * @lrb - pointer to local reference block
+ */
+static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
+{
+ int len;
+ if (lrbp->sense_buffer) {
+ len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
+ memcpy(lrbp->sense_buffer,
+ lrbp->ucd_rsp_ptr->sense_data,
+ min_t(int, len, SCSI_SENSE_BUFFERSIZE));
+ }
+}
+
+/**
+ * ufshcd_hba_capabilities - Read controller capabilities
+ * @hba: per adapter instance
+ */
+static inline void ufshcd_hba_capabilities(struct ufs_hba *hba)
+{
+ hba->capabilities =
+ readl(hba->mmio_base + REG_CONTROLLER_CAPABILITIES);
+
+ /* nutrs and nutmrs are 0 based values */
+ hba->nutrs = (hba->capabilities & MASK_TRANSFER_REQUESTS_SLOTS) + 1;
+ hba->nutmrs =
+ ((hba->capabilities & MASK_TASK_MANAGEMENT_REQUEST_SLOTS) >> 16) + 1;
+}
+
+/**
+ * ufshcd_send_uic_command - Send UIC commands to unipro layers
+ * @hba: per adapter instance
+ * @uic_command: UIC command
+ */
+static inline void
+ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd)
+{
+ /* Write Args */
+ writel(uic_cmnd->argument1,
+ (hba->mmio_base + REG_UIC_COMMAND_ARG_1));
+ writel(uic_cmnd->argument2,
+ (hba->mmio_base + REG_UIC_COMMAND_ARG_2));
+ writel(uic_cmnd->argument3,
+ (hba->mmio_base + REG_UIC_COMMAND_ARG_3));
+
+ /* Write UIC Cmd */
+ writel((uic_cmnd->command & COMMAND_OPCODE_MASK),
+ (hba->mmio_base + REG_UIC_COMMAND));
+}
+
+/**
+ * ufshcd_map_sg - Map scatter-gather list to prdt
+ * @lrbp - pointer to local reference block
+ *
+ * Returns 0 in case of success, non-zero value in case of failure
+ */
+static int ufshcd_map_sg(struct ufshcd_lrb *lrbp)
+{
+ struct ufshcd_sg_entry *prd_table;
+ struct scatterlist *sg;
+ struct scsi_cmnd *cmd;
+ int sg_segments;
+ int i;
+
+ cmd = lrbp->cmd;
+ sg_segments = scsi_dma_map(cmd);
+ if (sg_segments < 0)
+ return sg_segments;
+
+ if (sg_segments) {
+ lrbp->utr_descriptor_ptr->prd_table_length =
+ cpu_to_le16((u16) (sg_segments));
+
+ prd_table = (struct ufshcd_sg_entry *)lrbp->ucd_prdt_ptr;
+
+ scsi_for_each_sg(cmd, sg, sg_segments, i) {
+ prd_table[i].size =
+ cpu_to_le32(((u32) sg_dma_len(sg))-1);
+ prd_table[i].base_addr =
+ cpu_to_le32(lower_32_bits(sg->dma_address));
+ prd_table[i].upper_addr =
+ cpu_to_le32(upper_32_bits(sg->dma_address));
+ }
+ } else {
+ lrbp->utr_descriptor_ptr->prd_table_length = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * ufshcd_int_config - enable/disable interrupts
+ * @hba: per adapter instance
+ * @option: interrupt option
+ */
+static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
+{
+ switch (option) {
+ case UFSHCD_INT_ENABLE:
+ writel(hba->int_enable_mask,
+ (hba->mmio_base + REG_INTERRUPT_ENABLE));
+ break;
+ case UFSHCD_INT_DISABLE:
+ if (hba->ufs_version == UFSHCI_VERSION_10)
+ writel(INTERRUPT_DISABLE_MASK_10,
+ (hba->mmio_base + REG_INTERRUPT_ENABLE));
+ else
+ writel(INTERRUPT_DISABLE_MASK_11,
+ (hba->mmio_base + REG_INTERRUPT_ENABLE));
+ break;
+ }
+}
+
+/**
+ * ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU)
+ * @lrb - pointer to local reference block
+ */
+static void ufshcd_compose_upiu(struct ufshcd_lrb *lrbp)
+{
+ struct utp_transfer_req_desc *req_desc;
+ struct utp_upiu_cmd *ucd_cmd_ptr;
+ u32 data_direction;
+ u32 upiu_flags;
+
+ ucd_cmd_ptr = lrbp->ucd_cmd_ptr;
+ req_desc = lrbp->utr_descriptor_ptr;
+
+ switch (lrbp->command_type) {
+ case UTP_CMD_TYPE_SCSI:
+ if (lrbp->cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ data_direction = UTP_DEVICE_TO_HOST;
+ upiu_flags = UPIU_CMD_FLAGS_READ;
+ } else if (lrbp->cmd->sc_data_direction == DMA_TO_DEVICE) {
+ data_direction = UTP_HOST_TO_DEVICE;
+ upiu_flags = UPIU_CMD_FLAGS_WRITE;
+ } else {
+ data_direction = UTP_NO_DATA_TRANSFER;
+ upiu_flags = UPIU_CMD_FLAGS_NONE;
+ }
+
+ /* Transfer request descriptor header fields */
+ req_desc->header.dword_0 =
+ cpu_to_le32(data_direction | UTP_SCSI_COMMAND);
+
+ /*
+ * assigning invalid value for command status. Controller
+ * updates OCS on command completion, with the command
+ * status
+ */
+ req_desc->header.dword_2 =
+ cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
+
+ /* command descriptor fields */
+ ucd_cmd_ptr->header.dword_0 =
+ cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_COMMAND,
+ upiu_flags,
+ lrbp->lun,
+ lrbp->task_tag));
+ ucd_cmd_ptr->header.dword_1 =
+ cpu_to_be32(
+ UPIU_HEADER_DWORD(UPIU_COMMAND_SET_TYPE_SCSI,
+ 0,
+ 0,
+ 0));
+
+ /* Total EHS length and Data segment length will be zero */
+ ucd_cmd_ptr->header.dword_2 = 0;
+
+ ucd_cmd_ptr->exp_data_transfer_len =
+ cpu_to_be32(lrbp->cmd->transfersize);
+
+ memcpy(ucd_cmd_ptr->cdb,
+ lrbp->cmd->cmnd,
+ (min_t(unsigned short,
+ lrbp->cmd->cmd_len,
+ MAX_CDB_SIZE)));
+ break;
+ case UTP_CMD_TYPE_DEV_MANAGE:
+ /* For query function implementation */
+ break;
+ case UTP_CMD_TYPE_UFS:
+ /* For UFS native command implementation */
+ break;
+ } /* end of switch */
+}
+
+/**
+ * ufshcd_queuecommand - main entry point for SCSI requests
+ * @cmd: command from SCSI Midlayer
+ * @done: call back function
+ *
+ * Returns 0 for success, non-zero in case of failure
+ */
+static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
+{
+ struct ufshcd_lrb *lrbp;
+ struct ufs_hba *hba;
+ unsigned long flags;
+ int tag;
+ int err = 0;
+
+ hba = shost_priv(host);
+
+ tag = cmd->request->tag;
+
+ if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) {
+ err = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+
+ lrbp = &hba->lrb[tag];
+
+ lrbp->cmd = cmd;
+ lrbp->sense_bufflen = SCSI_SENSE_BUFFERSIZE;
+ lrbp->sense_buffer = cmd->sense_buffer;
+ lrbp->task_tag = tag;
+ lrbp->lun = cmd->device->lun;
+
+ lrbp->command_type = UTP_CMD_TYPE_SCSI;
+
+ /* form UPIU before issuing the command */
+ ufshcd_compose_upiu(lrbp);
+ err = ufshcd_map_sg(lrbp);
+ if (err)
+ goto out;
+
+ /* issue command to the controller */
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ ufshcd_send_command(hba, tag);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+out:
+ return err;
+}
+
+/**
+ * ufshcd_memory_alloc - allocate memory for host memory space data structures
+ * @hba: per adapter instance
+ *
+ * 1. Allocate DMA memory for Command Descriptor array
+ * Each command descriptor consist of Command UPIU, Response UPIU and PRDT
+ * 2. Allocate DMA memory for UTP Transfer Request Descriptor List (UTRDL).
+ * 3. Allocate DMA memory for UTP Task Management Request Descriptor List
+ * (UTMRDL)
+ * 4. Allocate memory for local reference block(lrb).
+ *
+ * Returns 0 for success, non-zero in case of failure
+ */
+static int ufshcd_memory_alloc(struct ufs_hba *hba)
+{
+ size_t utmrdl_size, utrdl_size, ucdl_size;
+
+ /* Allocate memory for UTP command descriptors */
+ ucdl_size = (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs);
+ hba->ucdl_base_addr = dma_alloc_coherent(&hba->pdev->dev,
+ ucdl_size,
+ &hba->ucdl_dma_addr,
+ GFP_KERNEL);
+
+ /*
+ * UFSHCI requires UTP command descriptor to be 128 byte aligned.
+ * make sure hba->ucdl_dma_addr is aligned to PAGE_SIZE
+ * if hba->ucdl_dma_addr is aligned to PAGE_SIZE, then it will
+ * be aligned to 128 bytes as well
+ */
+ if (!hba->ucdl_base_addr ||
+ WARN_ON(hba->ucdl_dma_addr & (PAGE_SIZE - 1))) {
+ dev_err(&hba->pdev->dev,
+ "Command Descriptor Memory allocation failed\n");
+ goto out;
+ }
+
+ /*
+ * Allocate memory for UTP Transfer descriptors
+ * UFSHCI requires 1024 byte alignment of UTRD
+ */
+ utrdl_size = (sizeof(struct utp_transfer_req_desc) * hba->nutrs);
+ hba->utrdl_base_addr = dma_alloc_coherent(&hba->pdev->dev,
+ utrdl_size,
+ &hba->utrdl_dma_addr,
+ GFP_KERNEL);
+ if (!hba->utrdl_base_addr ||
+ WARN_ON(hba->utrdl_dma_addr & (PAGE_SIZE - 1))) {
+ dev_err(&hba->pdev->dev,
+ "Transfer Descriptor Memory allocation failed\n");
+ goto out;
+ }
+
+ /*
+ * Allocate memory for UTP Task Management descriptors
+ * UFSHCI requires 1024 byte alignment of UTMRD
+ */
+ utmrdl_size = sizeof(struct utp_task_req_desc) * hba->nutmrs;
+ hba->utmrdl_base_addr = dma_alloc_coherent(&hba->pdev->dev,
+ utmrdl_size,
+ &hba->utmrdl_dma_addr,
+ GFP_KERNEL);
+ if (!hba->utmrdl_base_addr ||
+ WARN_ON(hba->utmrdl_dma_addr & (PAGE_SIZE - 1))) {
+ dev_err(&hba->pdev->dev,
+ "Task Management Descriptor Memory allocation failed\n");
+ goto out;
+ }
+
+ /* Allocate memory for local reference block */
+ hba->lrb = kcalloc(hba->nutrs, sizeof(struct ufshcd_lrb), GFP_KERNEL);
+ if (!hba->lrb) {
+ dev_err(&hba->pdev->dev, "LRB Memory allocation failed\n");
+ goto out;
+ }
+ return 0;
+out:
+ ufshcd_free_hba_memory(hba);
+ return -ENOMEM;
+}
+
+/**
+ * ufshcd_host_memory_configure - configure local reference block with
+ * memory offsets
+ * @hba: per adapter instance
+ *
+ * Configure Host memory space
+ * 1. Update Corresponding UTRD.UCDBA and UTRD.UCDBAU with UCD DMA
+ * address.
+ * 2. Update each UTRD with Response UPIU offset, Response UPIU length
+ * and PRDT offset.
+ * 3. Save the corresponding addresses of UTRD, UCD.CMD, UCD.RSP and UCD.PRDT
+ * into local reference block.
+ */
+static void ufshcd_host_memory_configure(struct ufs_hba *hba)
+{
+ struct utp_transfer_cmd_desc *cmd_descp;
+ struct utp_transfer_req_desc *utrdlp;
+ dma_addr_t cmd_desc_dma_addr;
+ dma_addr_t cmd_desc_element_addr;
+ u16 response_offset;
+ u16 prdt_offset;
+ int cmd_desc_size;
+ int i;
+
+ utrdlp = hba->utrdl_base_addr;
+ cmd_descp = hba->ucdl_base_addr;
+
+ response_offset =
+ offsetof(struct utp_transfer_cmd_desc, response_upiu);
+ prdt_offset =
+ offsetof(struct utp_transfer_cmd_desc, prd_table);
+
+ cmd_desc_size = sizeof(struct utp_transfer_cmd_desc);
+ cmd_desc_dma_addr = hba->ucdl_dma_addr;
+
+ for (i = 0; i < hba->nutrs; i++) {
+ /* Configure UTRD with command descriptor base address */
+ cmd_desc_element_addr =
+ (cmd_desc_dma_addr + (cmd_desc_size * i));
+ utrdlp[i].command_desc_base_addr_lo =
+ cpu_to_le32(lower_32_bits(cmd_desc_element_addr));
+ utrdlp[i].command_desc_base_addr_hi =
+ cpu_to_le32(upper_32_bits(cmd_desc_element_addr));
+
+ /* Response upiu and prdt offset should be in double words */
+ utrdlp[i].response_upiu_offset =
+ cpu_to_le16((response_offset >> 2));
+ utrdlp[i].prd_table_offset =
+ cpu_to_le16((prdt_offset >> 2));
+ utrdlp[i].response_upiu_length =
+ cpu_to_le16(ALIGNED_UPIU_SIZE);
+
+ hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
+ hba->lrb[i].ucd_cmd_ptr =
+ (struct utp_upiu_cmd *)(cmd_descp + i);
+ hba->lrb[i].ucd_rsp_ptr =
+ (struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
+ hba->lrb[i].ucd_prdt_ptr =
+ (struct ufshcd_sg_entry *)cmd_descp[i].prd_table;
+ }
+}
+
+/**
+ * ufshcd_dme_link_startup - Notify Unipro to perform link startup
+ * @hba: per adapter instance
+ *
+ * UIC_CMD_DME_LINK_STARTUP command must be issued to Unipro layer,
+ * in order to initialize the Unipro link startup procedure.
+ * Once the Unipro links are up, the device connected to the controller
+ * is detected.
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_dme_link_startup(struct ufs_hba *hba)
+{
+ struct uic_command *uic_cmd;
+ unsigned long flags;
+
+ /* check if controller is ready to accept UIC commands */
+ if (((readl(hba->mmio_base + REG_CONTROLLER_STATUS)) &
+ UIC_COMMAND_READY) == 0x0) {
+ dev_err(&hba->pdev->dev,
+ "Controller not ready"
+ " to accept UIC commands\n");
+ return -EIO;
+ }
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+
+ /* form UIC command */
+ uic_cmd = &hba->active_uic_cmd;
+ uic_cmd->command = UIC_CMD_DME_LINK_STARTUP;
+ uic_cmd->argument1 = 0;
+ uic_cmd->argument2 = 0;
+ uic_cmd->argument3 = 0;
+
+ /* enable UIC related interrupts */
+ hba->int_enable_mask |= UIC_COMMAND_COMPL;
+ ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
+
+ /* sending UIC commands to controller */
+ ufshcd_send_uic_command(hba, uic_cmd);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ return 0;
+}
+
+/**
+ * ufshcd_make_hba_operational - Make UFS controller operational
+ * @hba: per adapter instance
+ *
+ * To bring UFS host controller to operational state,
+ * 1. Check if device is present
+ * 2. Configure run-stop-registers
+ * 3. Enable required interrupts
+ * 4. Configure interrupt aggregation
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_make_hba_operational(struct ufs_hba *hba)
+{
+ int err = 0;
+ u32 reg;
+
+ /* check if device present */
+ reg = readl((hba->mmio_base + REG_CONTROLLER_STATUS));
+ if (ufshcd_is_device_present(reg)) {
+ dev_err(&hba->pdev->dev, "cc: Device not present\n");
+ err = -ENXIO;
+ goto out;
+ }
+
+ /*
+ * UCRDY, UTMRLDY and UTRLRDY bits must be 1
+ * DEI, HEI bits must be 0
+ */
+ if (!(ufshcd_get_lists_status(reg))) {
+ ufshcd_enable_run_stop_reg(hba);
+ } else {
+ dev_err(&hba->pdev->dev,
+ "Host controller not ready to process requests");
+ err = -EIO;
+ goto out;
+ }
+
+ /* Enable required interrupts */
+ hba->int_enable_mask |= (UTP_TRANSFER_REQ_COMPL |
+ UIC_ERROR |
+ UTP_TASK_REQ_COMPL |
+ DEVICE_FATAL_ERROR |
+ CONTROLLER_FATAL_ERROR |
+ SYSTEM_BUS_FATAL_ERROR);
+ ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
+
+ /* Configure interrupt aggregation */
+ ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
+
+ if (hba->ufshcd_state == UFSHCD_STATE_RESET)
+ scsi_unblock_requests(hba->host);
+
+ hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
+ scsi_scan_host(hba->host);
+out:
+ return err;
+}
+
+/**
+ * ufshcd_hba_enable - initialize the controller
+ * @hba: per adapter instance
+ *
+ * The controller resets itself and controller firmware initialization
+ * sequence kicks off. When controller is ready it will set
+ * the Host Controller Enable bit to 1.
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_hba_enable(struct ufs_hba *hba)
+{
+ int retry;
+
+ /*
+ * msleep of 1 and 5 used in this function might result in msleep(20),
+ * but it was necessary to send the UFS FPGA to reset mode during
+ * development and testing of this driver. msleep can be changed to
+ * mdelay and retry count can be reduced based on the controller.
+ */
+ if (!ufshcd_is_hba_active(hba)) {
+
+ /* change controller state to "reset state" */
+ ufshcd_hba_stop(hba);
+
+ /*
+ * This delay is based on the testing done with UFS host
+ * controller FPGA. The delay can be changed based on the
+ * host controller used.
+ */
+ msleep(5);
+ }
+
+ /* start controller initialization sequence */
+ ufshcd_hba_start(hba);
+
+ /*
+ * To initialize a UFS host controller HCE bit must be set to 1.
+ * During initialization the HCE bit value changes from 1->0->1.
+ * When the host controller completes initialization sequence
+ * it sets the value of HCE bit to 1. The same HCE bit is read back
+ * to check if the controller has completed initialization sequence.
+ * So without this delay the value HCE = 1, set in the previous
+ * instruction might be read back.
+ * This delay can be changed based on the controller.
+ */
+ msleep(1);
+
+ /* wait for the host controller to complete initialization */
+ retry = 10;
+ while (ufshcd_is_hba_active(hba)) {
+ if (retry) {
+ retry--;
+ } else {
+ dev_err(&hba->pdev->dev,
+ "Controller enable failed\n");
+ return -EIO;
+ }
+ msleep(5);
+ }
+ return 0;
+}
+
+/**
+ * ufshcd_initialize_hba - start the initialization process
+ * @hba: per adapter instance
+ *
+ * 1. Enable the controller via ufshcd_hba_enable.
+ * 2. Program the Transfer Request List Address with the starting address of
+ * UTRDL.
+ * 3. Program the Task Management Request List Address with starting address
+ * of UTMRDL.
+ *
+ * Returns 0 on success, non-zero value on failure.
+ */
+static int ufshcd_initialize_hba(struct ufs_hba *hba)
+{
+ if (ufshcd_hba_enable(hba))
+ return -EIO;
+
+ /* Configure UTRL and UTMRL base address registers */
+ writel(hba->utrdl_dma_addr,
+ (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_BASE_L));
+ writel(lower_32_bits(hba->utrdl_dma_addr),
+ (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_BASE_H));
+ writel(hba->utmrdl_dma_addr,
+ (hba->mmio_base + REG_UTP_TASK_REQ_LIST_BASE_L));
+ writel(upper_32_bits(hba->utmrdl_dma_addr),
+ (hba->mmio_base + REG_UTP_TASK_REQ_LIST_BASE_H));
+
+ /* Initialize unipro link startup procedure */
+ return ufshcd_dme_link_startup(hba);
+}
+
+/**
+ * ufshcd_do_reset - reset the host controller
+ * @hba: per adapter instance
+ *
+ * Returns SUCCESS/FAILED
+ */
+static int ufshcd_do_reset(struct ufs_hba *hba)
+{
+ struct ufshcd_lrb *lrbp;
+ unsigned long flags;
+ int tag;
+
+ /* block commands from midlayer */
+ scsi_block_requests(hba->host);
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ hba->ufshcd_state = UFSHCD_STATE_RESET;
+
+ /* send controller to reset state */
+ ufshcd_hba_stop(hba);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ /* abort outstanding commands */
+ for (tag = 0; tag < hba->nutrs; tag++) {
+ if (test_bit(tag, &hba->outstanding_reqs)) {
+ lrbp = &hba->lrb[tag];
+ scsi_dma_unmap(lrbp->cmd);
+ lrbp->cmd->result = DID_RESET << 16;
+ lrbp->cmd->scsi_done(lrbp->cmd);
+ lrbp->cmd = NULL;
+ }
+ }
+
+ /* clear outstanding request/task bit maps */
+ hba->outstanding_reqs = 0;
+ hba->outstanding_tasks = 0;
+
+ /* start the initialization process */
+ if (ufshcd_initialize_hba(hba)) {
+ dev_err(&hba->pdev->dev,
+ "Reset: Controller initialization failed\n");
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * ufshcd_slave_alloc - handle initial SCSI device configurations
+ * @sdev: pointer to SCSI device
+ *
+ * Returns success
+ */
+static int ufshcd_slave_alloc(struct scsi_device *sdev)
+{
+ struct ufs_hba *hba;
+
+ hba = shost_priv(sdev->host);
+ sdev->tagged_supported = 1;
+
+ /* Mode sense(6) is not supported by UFS, so use Mode sense(10) */
+ sdev->use_10_for_ms = 1;
+ scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
+
+ /*
+ * Inform SCSI Midlayer that the LUN queue depth is same as the
+ * controller queue depth. If a LUN queue depth is less than the
+ * controller queue depth and if the LUN reports
+ * SAM_STAT_TASK_SET_FULL, the LUN queue depth will be adjusted
+ * with scsi_adjust_queue_depth.
+ */
+ scsi_activate_tcq(sdev, hba->nutrs);
+ return 0;
+}
+
+/**
+ * ufshcd_slave_destroy - remove SCSI device configurations
+ * @sdev: pointer to SCSI device
+ */
+static void ufshcd_slave_destroy(struct scsi_device *sdev)
+{
+ struct ufs_hba *hba;
+
+ hba = shost_priv(sdev->host);
+ scsi_deactivate_tcq(sdev, hba->nutrs);
+}
+
+/**
+ * ufshcd_task_req_compl - handle task management request completion
+ * @hba: per adapter instance
+ * @index: index of the completed request
+ *
+ * Returns SUCCESS/FAILED
+ */
+static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index)
+{
+ struct utp_task_req_desc *task_req_descp;
+ struct utp_upiu_task_rsp *task_rsp_upiup;
+ unsigned long flags;
+ int ocs_value;
+ int task_result;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+
+ /* Clear completed tasks from outstanding_tasks */
+ __clear_bit(index, &hba->outstanding_tasks);
+
+ task_req_descp = hba->utmrdl_base_addr;
+ ocs_value = ufshcd_get_tmr_ocs(&task_req_descp[index]);
+
+ if (ocs_value == OCS_SUCCESS) {
+ task_rsp_upiup = (struct utp_upiu_task_rsp *)
+ task_req_descp[index].task_rsp_upiu;
+ task_result = be32_to_cpu(task_rsp_upiup->header.dword_1);
+ task_result = ((task_result & MASK_TASK_RESPONSE) >> 8);
+
+ if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL ||
+ task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED)
+ task_result = FAILED;
+ } else {
+ task_result = FAILED;
+ dev_err(&hba->pdev->dev,
+ "trc: Invalid ocs = %x\n", ocs_value);
+ }
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ return task_result;
+}
+
+/**
+ * ufshcd_adjust_lun_qdepth - Update LUN queue depth if device responds with
+ * SAM_STAT_TASK_SET_FULL SCSI command status.
+ * @cmd: pointer to SCSI command
+ */
+static void ufshcd_adjust_lun_qdepth(struct scsi_cmnd *cmd)
+{
+ struct ufs_hba *hba;
+ int i;
+ int lun_qdepth = 0;
+
+ hba = shost_priv(cmd->device->host);
+
+ /*
+ * LUN queue depth can be obtained by counting outstanding commands
+ * on the LUN.
+ */
+ for (i = 0; i < hba->nutrs; i++) {
+ if (test_bit(i, &hba->outstanding_reqs)) {
+
+ /*
+ * Check if the outstanding command belongs
+ * to the LUN which reported SAM_STAT_TASK_SET_FULL.
+ */
+ if (cmd->device->lun == hba->lrb[i].lun)
+ lun_qdepth++;
+ }
+ }
+
+ /*
+ * LUN queue depth will be total outstanding commands, except the
+ * command for which the LUN reported SAM_STAT_TASK_SET_FULL.
+ */
+ scsi_adjust_queue_depth(cmd->device, MSG_SIMPLE_TAG, lun_qdepth - 1);
+}
+
+/**
+ * ufshcd_scsi_cmd_status - Update SCSI command result based on SCSI status
+ * @lrb: pointer to local reference block of completed command
+ * @scsi_status: SCSI command status
+ *
+ * Returns value base on SCSI command status
+ */
+static inline int
+ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
+{
+ int result = 0;
+
+ switch (scsi_status) {
+ case SAM_STAT_GOOD:
+ result |= DID_OK << 16 |
+ COMMAND_COMPLETE << 8 |
+ SAM_STAT_GOOD;
+ break;
+ case SAM_STAT_CHECK_CONDITION:
+ result |= DID_OK << 16 |
+ COMMAND_COMPLETE << 8 |
+ SAM_STAT_CHECK_CONDITION;
+ ufshcd_copy_sense_data(lrbp);
+ break;
+ case SAM_STAT_BUSY:
+ result |= SAM_STAT_BUSY;
+ break;
+ case SAM_STAT_TASK_SET_FULL:
+
+ /*
+ * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN queue
+ * depth needs to be adjusted to the exact number of
+ * outstanding commands the LUN can handle at any given time.
+ */
+ ufshcd_adjust_lun_qdepth(lrbp->cmd);
+ result |= SAM_STAT_TASK_SET_FULL;
+ break;
+ case SAM_STAT_TASK_ABORTED:
+ result |= SAM_STAT_TASK_ABORTED;
+ break;
+ default:
+ result |= DID_ERROR << 16;
+ break;
+ } /* end of switch */
+
+ return result;
+}
+
+/**
+ * ufshcd_transfer_rsp_status - Get overall status of the response
+ * @hba: per adapter instance
+ * @lrb: pointer to local reference block of completed command
+ *
+ * Returns result of the command to notify SCSI midlayer
+ */
+static inline int
+ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+{
+ int result = 0;
+ int scsi_status;
+ int ocs;
+
+ /* overall command status of utrd */
+ ocs = ufshcd_get_tr_ocs(lrbp);
+
+ switch (ocs) {
+ case OCS_SUCCESS:
+
+ /* check if the returned transfer response is valid */
+ result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
+ if (result) {
+ dev_err(&hba->pdev->dev,
+ "Invalid response = %x\n", result);
+ break;
+ }
+
+ /*
+ * get the response UPIU result to extract
+ * the SCSI command status
+ */
+ result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
+
+ /*
+ * get the result based on SCSI status response
+ * to notify the SCSI midlayer of the command status
+ */
+ scsi_status = result & MASK_SCSI_STATUS;
+ result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
+ break;
+ case OCS_ABORTED:
+ result |= DID_ABORT << 16;
+ break;
+ case OCS_INVALID_CMD_TABLE_ATTR:
+ case OCS_INVALID_PRDT_ATTR:
+ case OCS_MISMATCH_DATA_BUF_SIZE:
+ case OCS_MISMATCH_RESP_UPIU_SIZE:
+ case OCS_PEER_COMM_FAILURE:
+ case OCS_FATAL_ERROR:
+ default:
+ result |= DID_ERROR << 16;
+ dev_err(&hba->pdev->dev,
+ "OCS error from controller = %x\n", ocs);
+ break;
+ } /* end of switch */
+
+ return result;
+}
+
+/**
+ * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * @hba: per adapter instance
+ */
+static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+{
+ struct ufshcd_lrb *lrb;
+ unsigned long completed_reqs;
+ u32 tr_doorbell;
+ int result;
+ int index;
+
+ lrb = hba->lrb;
+ tr_doorbell =
+ readl(hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL);
+ completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
+
+ for (index = 0; index < hba->nutrs; index++) {
+ if (test_bit(index, &completed_reqs)) {
+
+ result = ufshcd_transfer_rsp_status(hba, &lrb[index]);
+
+ if (lrb[index].cmd) {
+ scsi_dma_unmap(lrb[index].cmd);
+ lrb[index].cmd->result = result;
+ lrb[index].cmd->scsi_done(lrb[index].cmd);
+
+ /* Mark completed command as NULL in LRB */
+ lrb[index].cmd = NULL;
+ }
+ } /* end of if */
+ } /* end of for */
+
+ /* clear corresponding bits of completed commands */
+ hba->outstanding_reqs ^= completed_reqs;
+
+ /* Reset interrupt aggregation counters */
+ ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
+}
+
+/**
+ * ufshcd_uic_cc_handler - handle UIC command completion
+ * @work: pointer to a work queue structure
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static void ufshcd_uic_cc_handler (struct work_struct *work)
+{
+ struct ufs_hba *hba;
+
+ hba = container_of(work, struct ufs_hba, uic_workq);
+
+ if ((hba->active_uic_cmd.command == UIC_CMD_DME_LINK_STARTUP) &&
+ !(ufshcd_get_uic_cmd_result(hba))) {
+
+ if (ufshcd_make_hba_operational(hba))
+ dev_err(&hba->pdev->dev,
+ "cc: hba not operational state\n");
+ return;
+ }
+}
+
+/**
+ * ufshcd_fatal_err_handler - handle fatal errors
+ * @hba: per adapter instance
+ */
+static void ufshcd_fatal_err_handler(struct work_struct *work)
+{
+ struct ufs_hba *hba;
+ hba = container_of(work, struct ufs_hba, feh_workq);
+
+ /* check if reset is already in progress */
+ if (hba->ufshcd_state != UFSHCD_STATE_RESET)
+ ufshcd_do_reset(hba);
+}
+
+/**
+ * ufshcd_err_handler - Check for fatal errors
+ * @work: pointer to a work queue structure
+ */
+static void ufshcd_err_handler(struct ufs_hba *hba)
+{
+ u32 reg;
+
+ if (hba->errors & INT_FATAL_ERRORS)
+ goto fatal_eh;
+
+ if (hba->errors & UIC_ERROR) {
+
+ reg = readl(hba->mmio_base +
+ REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
+ if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
+ goto fatal_eh;
+ }
+ return;
+fatal_eh:
+ hba->ufshcd_state = UFSHCD_STATE_ERROR;
+ schedule_work(&hba->feh_workq);
+}
+
+/**
+ * ufshcd_tmc_handler - handle task management function completion
+ * @hba: per adapter instance
+ */
+static void ufshcd_tmc_handler(struct ufs_hba *hba)
+{
+ u32 tm_doorbell;
+
+ tm_doorbell = readl(hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL);
+ hba->tm_condition = tm_doorbell ^ hba->outstanding_tasks;
+ wake_up_interruptible(&hba->ufshcd_tm_wait_queue);
+}
+
+/**
+ * ufshcd_sl_intr - Interrupt service routine
+ * @hba: per adapter instance
+ * @intr_status: contains interrupts generated by the controller
+ */
+static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
+{
+ hba->errors = UFSHCD_ERROR_MASK & intr_status;
+ if (hba->errors)
+ ufshcd_err_handler(hba);
+
+ if (intr_status & UIC_COMMAND_COMPL)
+ schedule_work(&hba->uic_workq);
+
+ if (intr_status & UTP_TASK_REQ_COMPL)
+ ufshcd_tmc_handler(hba);
+
+ if (intr_status & UTP_TRANSFER_REQ_COMPL)
+ ufshcd_transfer_req_compl(hba);
+}
+
+/**
+ * ufshcd_intr - Main interrupt service routine
+ * @irq: irq number
+ * @__hba: pointer to adapter instance
+ *
+ * Returns IRQ_HANDLED - If interrupt is valid
+ * IRQ_NONE - If invalid interrupt
+ */
+static irqreturn_t ufshcd_intr(int irq, void *__hba)
+{
+ u32 intr_status;
+ irqreturn_t retval = IRQ_NONE;
+ struct ufs_hba *hba = __hba;
+
+ spin_lock(hba->host->host_lock);
+ intr_status = readl(hba->mmio_base + REG_INTERRUPT_STATUS);
+
+ if (intr_status) {
+ ufshcd_sl_intr(hba, intr_status);
+
+ /* If UFSHCI 1.0 then clear interrupt status register */
+ if (hba->ufs_version == UFSHCI_VERSION_10)
+ writel(intr_status,
+ (hba->mmio_base + REG_INTERRUPT_STATUS));
+ retval = IRQ_HANDLED;
+ }
+ spin_unlock(hba->host->host_lock);
+ return retval;
+}
+
+/**
+ * ufshcd_issue_tm_cmd - issues task management commands to controller
+ * @hba: per adapter instance
+ * @lrbp: pointer to local reference block
+ *
+ * Returns SUCCESS/FAILED
+ */
+static int
+ufshcd_issue_tm_cmd(struct ufs_hba *hba,
+ struct ufshcd_lrb *lrbp,
+ u8 tm_function)
+{
+ struct utp_task_req_desc *task_req_descp;
+ struct utp_upiu_task_req *task_req_upiup;
+ struct Scsi_Host *host;
+ unsigned long flags;
+ int free_slot = 0;
+ int err;
+
+ host = hba->host;
+
+ spin_lock_irqsave(host->host_lock, flags);
+
+ /* If task management queue is full */
+ free_slot = ufshcd_get_tm_free_slot(hba);
+ if (free_slot >= hba->nutmrs) {
+ spin_unlock_irqrestore(host->host_lock, flags);
+ dev_err(&hba->pdev->dev, "Task management queue full\n");
+ err = FAILED;
+ goto out;
+ }
+
+ task_req_descp = hba->utmrdl_base_addr;
+ task_req_descp += free_slot;
+
+ /* Configure task request descriptor */
+ task_req_descp->header.dword_0 = cpu_to_le32(UTP_REQ_DESC_INT_CMD);
+ task_req_descp->header.dword_2 =
+ cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
+
+ /* Configure task request UPIU */
+ task_req_upiup =
+ (struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
+ task_req_upiup->header.dword_0 =
+ cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
+ lrbp->lun, lrbp->task_tag));
+ task_req_upiup->header.dword_1 =
+ cpu_to_be32(UPIU_HEADER_DWORD(0, tm_function, 0, 0));
+
+ task_req_upiup->input_param1 = lrbp->lun;
+ task_req_upiup->input_param1 =
+ cpu_to_be32(task_req_upiup->input_param1);
+ task_req_upiup->input_param2 = lrbp->task_tag;
+ task_req_upiup->input_param2 =
+ cpu_to_be32(task_req_upiup->input_param2);
+
+ /* send command to the controller */
+ __set_bit(free_slot, &hba->outstanding_tasks);
+ writel((1 << free_slot),
+ (hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL));
+
+ spin_unlock_irqrestore(host->host_lock, flags);
+
+ /* wait until the task management command is completed */
+ err =
+ wait_event_interruptible_timeout(hba->ufshcd_tm_wait_queue,
+ (test_bit(free_slot,
+ &hba->tm_condition) != 0),
+ 60 * HZ);
+ if (!err) {
+ dev_err(&hba->pdev->dev,
+ "Task management command timed-out\n");
+ err = FAILED;
+ goto out;
+ }
+ clear_bit(free_slot, &hba->tm_condition);
+ return ufshcd_task_req_compl(hba, free_slot);
+out:
+ return err;
+}
+
+/**
+ * ufshcd_device_reset - reset device and abort all the pending commands
+ * @cmd: SCSI command pointer
+ *
+ * Returns SUCCESS/FAILED
+ */
+static int ufshcd_device_reset(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host;
+ struct ufs_hba *hba;
+ unsigned int tag;
+ u32 pos;
+ int err;
+
+ host = cmd->device->host;
+ hba = shost_priv(host);
+ tag = cmd->request->tag;
+
+ err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_LOGICAL_RESET);
+ if (err)
+ goto out;
+
+ for (pos = 0; pos < hba->nutrs; pos++) {
+ if (test_bit(pos, &hba->outstanding_reqs) &&
+ (hba->lrb[tag].lun == hba->lrb[pos].lun)) {
+
+ /* clear the respective UTRLCLR register bit */
+ ufshcd_utrl_clear(hba, pos);
+
+ clear_bit(pos, &hba->outstanding_reqs);
+
+ if (hba->lrb[pos].cmd) {
+ scsi_dma_unmap(hba->lrb[pos].cmd);
+ hba->lrb[pos].cmd->result =
+ DID_ABORT << 16;
+ hba->lrb[pos].cmd->scsi_done(cmd);
+ hba->lrb[pos].cmd = NULL;
+ }
+ }
+ } /* end of for */
+out:
+ return err;
+}
+
+/**
+ * ufshcd_host_reset - Main reset function registered with scsi layer
+ * @cmd: SCSI command pointer
+ *
+ * Returns SUCCESS/FAILED
+ */
+static int ufshcd_host_reset(struct scsi_cmnd *cmd)
+{
+ struct ufs_hba *hba;
+
+ hba = shost_priv(cmd->device->host);
+
+ if (hba->ufshcd_state == UFSHCD_STATE_RESET)
+ return SUCCESS;
+
+ return (ufshcd_do_reset(hba) == SUCCESS) ? SUCCESS : FAILED;
+}
+
+/**
+ * ufshcd_abort - abort a specific command
+ * @cmd: SCSI command pointer
+ *
+ * Returns SUCCESS/FAILED
+ */
+static int ufshcd_abort(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host;
+ struct ufs_hba *hba;
+ unsigned long flags;
+ unsigned int tag;
+ int err;
+
+ host = cmd->device->host;
+ hba = shost_priv(host);
+ tag = cmd->request->tag;
+
+ spin_lock_irqsave(host->host_lock, flags);
+
+ /* check if command is still pending */
+ if (!(test_bit(tag, &hba->outstanding_reqs))) {
+ err = FAILED;
+ spin_unlock_irqrestore(host->host_lock, flags);
+ goto out;
+ }
+ spin_unlock_irqrestore(host->host_lock, flags);
+
+ err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_ABORT_TASK);
+ if (err)
+ goto out;
+
+ scsi_dma_unmap(cmd);
+
+ spin_lock_irqsave(host->host_lock, flags);
+
+ /* clear the respective UTRLCLR register bit */
+ ufshcd_utrl_clear(hba, tag);
+
+ __clear_bit(tag, &hba->outstanding_reqs);
+ hba->lrb[tag].cmd = NULL;
+ spin_unlock_irqrestore(host->host_lock, flags);
+out:
+ return err;
+}
+
+static struct scsi_host_template ufshcd_driver_template = {
+ .module = THIS_MODULE,
+ .name = UFSHCD,
+ .proc_name = UFSHCD,
+ .queuecommand = ufshcd_queuecommand,
+ .slave_alloc = ufshcd_slave_alloc,
+ .slave_destroy = ufshcd_slave_destroy,
+ .eh_abort_handler = ufshcd_abort,
+ .eh_device_reset_handler = ufshcd_device_reset,
+ .eh_host_reset_handler = ufshcd_host_reset,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = UFSHCD_CMD_PER_LUN,
+ .can_queue = UFSHCD_CAN_QUEUE,
+};
+
+/**
+ * ufshcd_shutdown - main function to put the controller in reset state
+ * @pdev: pointer to PCI device handle
+ */
+static void ufshcd_shutdown(struct pci_dev *pdev)
+{
+ ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev));
+}
+
+#ifdef CONFIG_PM
+/**
+ * ufshcd_suspend - suspend power management function
+ * @pdev: pointer to PCI device handle
+ * @state: power state
+ *
+ * Returns -ENOSYS
+ */
+static int ufshcd_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ /*
+ * TODO:
+ * 1. Block SCSI requests from SCSI midlayer
+ * 2. Change the internal driver state to non operational
+ * 3. Set UTRLRSR and UTMRLRSR bits to zero
+ * 4. Wait until outstanding commands are completed
+ * 5. Set HCE to zero to send the UFS host controller to reset state
+ */
+
+ return -ENOSYS;
+}
+
+/**
+ * ufshcd_resume - resume power management function
+ * @pdev: pointer to PCI device handle
+ *
+ * Returns -ENOSYS
+ */
+static int ufshcd_resume(struct pci_dev *pdev)
+{
+ /*
+ * TODO:
+ * 1. Set HCE to 1, to start the UFS host controller
+ * initialization process
+ * 2. Set UTRLRSR and UTMRLRSR bits to 1
+ * 3. Change the internal driver state to operational
+ * 4. Unblock SCSI requests from SCSI midlayer
+ */
+
+ return -ENOSYS;
+}
+#endif /* CONFIG_PM */
+
+/**
+ * ufshcd_hba_free - free allocated memory for
+ * host memory space data structures
+ * @hba: per adapter instance
+ */
+static void ufshcd_hba_free(struct ufs_hba *hba)
+{
+ iounmap(hba->mmio_base);
+ ufshcd_free_hba_memory(hba);
+ pci_release_regions(hba->pdev);
+}
+
+/**
+ * ufshcd_remove - de-allocate PCI/SCSI host and host memory space
+ * data structure memory
+ * @pdev - pointer to PCI handle
+ */
+static void ufshcd_remove(struct pci_dev *pdev)
+{
+ struct ufs_hba *hba = pci_get_drvdata(pdev);
+
+ /* disable interrupts */
+ ufshcd_int_config(hba, UFSHCD_INT_DISABLE);
+ free_irq(pdev->irq, hba);
+
+ ufshcd_hba_stop(hba);
+ ufshcd_hba_free(hba);
+
+ scsi_remove_host(hba->host);
+ scsi_host_put(hba->host);
+ pci_set_drvdata(pdev, NULL);
+ pci_clear_master(pdev);
+ pci_disable_device(pdev);
+}
+
+/**
+ * ufshcd_set_dma_mask - Set dma mask based on the controller
+ * addressing capability
+ * @pdev: PCI device structure
+ *
+ * Returns 0 for success, non-zero for failure
+ */
+static int ufshcd_set_dma_mask(struct ufs_hba *hba)
+{
+ int err;
+ u64 dma_mask;
+
+ /*
+ * If controller supports 64 bit addressing mode, then set the DMA
+ * mask to 64-bit, else set the DMA mask to 32-bit
+ */
+ if (hba->capabilities & MASK_64_ADDRESSING_SUPPORT)
+ dma_mask = DMA_BIT_MASK(64);
+ else
+ dma_mask = DMA_BIT_MASK(32);
+
+ err = pci_set_dma_mask(hba->pdev, dma_mask);
+ if (err)
+ return err;
+
+ err = pci_set_consistent_dma_mask(hba->pdev, dma_mask);
+
+ return err;
+}
+
+/**
+ * ufshcd_probe - probe routine of the driver
+ * @pdev: pointer to PCI device handle
+ * @id: PCI device id
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int __devinit
+ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct Scsi_Host *host;
+ struct ufs_hba *hba;
+ int err;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "pci_enable_device failed\n");
+ goto out_error;
+ }
+
+ pci_set_master(pdev);
+
+ host = scsi_host_alloc(&ufshcd_driver_template,
+ sizeof(struct ufs_hba));
+ if (!host) {
+ dev_err(&pdev->dev, "scsi_host_alloc failed\n");
+ err = -ENOMEM;
+ goto out_disable;
+ }
+ hba = shost_priv(host);
+
+ err = pci_request_regions(pdev, UFSHCD);
+ if (err < 0) {
+ dev_err(&pdev->dev, "request regions failed\n");
+ goto out_disable;
+ }
+
+ hba->mmio_base = pci_ioremap_bar(pdev, 0);
+ if (!hba->mmio_base) {
+ dev_err(&pdev->dev, "memory map failed\n");
+ err = -ENOMEM;
+ goto out_release_regions;
+ }
+
+ hba->host = host;
+ hba->pdev = pdev;
+
+ /* Read capabilities registers */
+ ufshcd_hba_capabilities(hba);
+
+ /* Get UFS version supported by the controller */
+ hba->ufs_version = ufshcd_get_ufs_version(hba);
+
+ err = ufshcd_set_dma_mask(hba);
+ if (err) {
+ dev_err(&pdev->dev, "set dma mask failed\n");
+ goto out_iounmap;
+ }
+
+ /* Allocate memory for host memory space */
+ err = ufshcd_memory_alloc(hba);
+ if (err) {
+ dev_err(&pdev->dev, "Memory allocation failed\n");
+ goto out_iounmap;
+ }
+
+ /* Configure LRB */
+ ufshcd_host_memory_configure(hba);
+
+ host->can_queue = hba->nutrs;
+ host->cmd_per_lun = hba->nutrs;
+ host->max_id = UFSHCD_MAX_ID;
+ host->max_lun = UFSHCD_MAX_LUNS;
+ host->max_channel = UFSHCD_MAX_CHANNEL;
+ host->unique_id = host->host_no;
+ host->max_cmd_len = MAX_CDB_SIZE;
+
+ /* Initailize wait queue for task management */
+ init_waitqueue_head(&hba->ufshcd_tm_wait_queue);
+
+ /* Initialize work queues */
+ INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
+ INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);
+
+ /* IRQ registration */
+ err = request_irq(pdev->irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
+ if (err) {
+ dev_err(&pdev->dev, "request irq failed\n");
+ goto out_lrb_free;
+ }
+
+ /* Enable SCSI tag mapping */
+ err = scsi_init_shared_tag_map(host, host->can_queue);
+ if (err) {
+ dev_err(&pdev->dev, "init shared queue failed\n");
+ goto out_free_irq;
+ }
+
+ pci_set_drvdata(pdev, hba);
+
+ err = scsi_add_host(host, &pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "scsi_add_host failed\n");
+ goto out_free_irq;
+ }
+
+ /* Initialization routine */
+ err = ufshcd_initialize_hba(hba);
+ if (err) {
+ dev_err(&pdev->dev, "Initialization failed\n");
+ goto out_free_irq;
+ }
+
+ return 0;
+
+out_free_irq:
+ free_irq(pdev->irq, hba);
+out_lrb_free:
+ ufshcd_free_hba_memory(hba);
+out_iounmap:
+ iounmap(hba->mmio_base);
+out_release_regions:
+ pci_release_regions(pdev);
+out_disable:
+ scsi_host_put(host);
+ pci_clear_master(pdev);
+ pci_disable_device(pdev);
+out_error:
+ return err;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = {
+ { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { } /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(pci, ufshcd_pci_tbl);
+
+static struct pci_driver ufshcd_pci_driver = {
+ .name = UFSHCD,
+ .id_table = ufshcd_pci_tbl,
+ .probe = ufshcd_probe,
+ .remove = __devexit_p(ufshcd_remove),
+ .shutdown = ufshcd_shutdown,
+#ifdef CONFIG_PM
+ .suspend = ufshcd_suspend,
+ .resume = ufshcd_resume,
+#endif
+};
+
+/**
+ * ufshcd_init - Driver registration routine
+ */
+static int __init ufshcd_init(void)
+{
+ return pci_register_driver(&ufshcd_pci_driver);
+}
+module_init(ufshcd_init);
+
+/**
+ * ufshcd_exit - Driver exit clean-up routine
+ */
+static void __exit ufshcd_exit(void)
+{
+ pci_unregister_driver(&ufshcd_pci_driver);
+}
+module_exit(ufshcd_exit);
+
+
+MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>, "
+ "Vinayak Holikatti <h.vinayak@samsung.com>");
+MODULE_DESCRIPTION("Generic UFS host controller driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(UFSHCD_DRIVER_VERSION);
diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
new file mode 100644
index 0000000..6e3510f
--- /dev/null
+++ b/drivers/scsi/ufs/ufshci.h
@@ -0,0 +1,376 @@
+/*
+ * Universal Flash Storage Host controller driver
+ *
+ * This code is based on drivers/scsi/ufs/ufshci.h
+ * Copyright (C) 2011-2012 Samsung India Software Operations
+ *
+ * Santosh Yaraganavi <santosh.sy@samsung.com>
+ * Vinayak Holikatti <h.vinayak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef _UFSHCI_H
+#define _UFSHCI_H
+
+enum {
+ TASK_REQ_UPIU_SIZE_DWORDS = 8,
+ TASK_RSP_UPIU_SIZE_DWORDS = 8,
+ ALIGNED_UPIU_SIZE = 128,
+};
+
+/* UFSHCI Registers */
+enum {
+ REG_CONTROLLER_CAPABILITIES = 0x00,
+ REG_UFS_VERSION = 0x08,
+ REG_CONTROLLER_DEV_ID = 0x10,
+ REG_CONTROLLER_PROD_ID = 0x14,
+ REG_INTERRUPT_STATUS = 0x20,
+ REG_INTERRUPT_ENABLE = 0x24,
+ REG_CONTROLLER_STATUS = 0x30,
+ REG_CONTROLLER_ENABLE = 0x34,
+ REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER = 0x38,
+ REG_UIC_ERROR_CODE_DATA_LINK_LAYER = 0x3C,
+ REG_UIC_ERROR_CODE_NETWORK_LAYER = 0x40,
+ REG_UIC_ERROR_CODE_TRANSPORT_LAYER = 0x44,
+ REG_UIC_ERROR_CODE_DME = 0x48,
+ REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL = 0x4C,
+ REG_UTP_TRANSFER_REQ_LIST_BASE_L = 0x50,
+ REG_UTP_TRANSFER_REQ_LIST_BASE_H = 0x54,
+ REG_UTP_TRANSFER_REQ_DOOR_BELL = 0x58,
+ REG_UTP_TRANSFER_REQ_LIST_CLEAR = 0x5C,
+ REG_UTP_TRANSFER_REQ_LIST_RUN_STOP = 0x60,
+ REG_UTP_TASK_REQ_LIST_BASE_L = 0x70,
+ REG_UTP_TASK_REQ_LIST_BASE_H = 0x74,
+ REG_UTP_TASK_REQ_DOOR_BELL = 0x78,
+ REG_UTP_TASK_REQ_LIST_CLEAR = 0x7C,
+ REG_UTP_TASK_REQ_LIST_RUN_STOP = 0x80,
+ REG_UIC_COMMAND = 0x90,
+ REG_UIC_COMMAND_ARG_1 = 0x94,
+ REG_UIC_COMMAND_ARG_2 = 0x98,
+ REG_UIC_COMMAND_ARG_3 = 0x9C,
+};
+
+/* Controller capability masks */
+enum {
+ MASK_TRANSFER_REQUESTS_SLOTS = 0x0000001F,
+ MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000,
+ MASK_64_ADDRESSING_SUPPORT = 0x01000000,
+ MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000,
+ MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000,
+};
+
+/* UFS Version 08h */
+#define MINOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 0)
+#define MAJOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 16)
+
+/* Controller UFSHCI version */
+enum {
+ UFSHCI_VERSION_10 = 0x00010000,
+ UFSHCI_VERSION_11 = 0x00010100,
+};
+
+/*
+ * HCDDID - Host Controller Identification Descriptor
+ * - Device ID and Device Class 10h
+ */
+#define DEVICE_CLASS UFS_MASK(0xFFFF, 0)
+#define DEVICE_ID UFS_MASK(0xFF, 24)
+
+/*
+ * HCPMID - Host Controller Identification Descriptor
+ * - Product/Manufacturer ID 14h
+ */
+#define MANUFACTURE_ID_MASK UFS_MASK(0xFFFF, 0)
+#define PRODUCT_ID_MASK UFS_MASK(0xFFFF, 16)
+
+#define UFS_BIT(x) (1L << (x))
+
+#define UTP_TRANSFER_REQ_COMPL UFS_BIT(0)
+#define UIC_DME_END_PT_RESET UFS_BIT(1)
+#define UIC_ERROR UFS_BIT(2)
+#define UIC_TEST_MODE UFS_BIT(3)
+#define UIC_POWER_MODE UFS_BIT(4)
+#define UIC_HIBERNATE_EXIT UFS_BIT(5)
+#define UIC_HIBERNATE_ENTER UFS_BIT(6)
+#define UIC_LINK_LOST UFS_BIT(7)
+#define UIC_LINK_STARTUP UFS_BIT(8)
+#define UTP_TASK_REQ_COMPL UFS_BIT(9)
+#define UIC_COMMAND_COMPL UFS_BIT(10)
+#define DEVICE_FATAL_ERROR UFS_BIT(11)
+#define CONTROLLER_FATAL_ERROR UFS_BIT(16)
+#define SYSTEM_BUS_FATAL_ERROR UFS_BIT(17)
+
+#define UFSHCD_ERROR_MASK (UIC_ERROR |\
+ DEVICE_FATAL_ERROR |\
+ CONTROLLER_FATAL_ERROR |\
+ SYSTEM_BUS_FATAL_ERROR)
+
+#define INT_FATAL_ERRORS (DEVICE_FATAL_ERROR |\
+ CONTROLLER_FATAL_ERROR |\
+ SYSTEM_BUS_FATAL_ERROR)
+
+/* HCS - Host Controller Status 30h */
+#define DEVICE_PRESENT UFS_BIT(0)
+#define UTP_TRANSFER_REQ_LIST_READY UFS_BIT(1)
+#define UTP_TASK_REQ_LIST_READY UFS_BIT(2)
+#define UIC_COMMAND_READY UFS_BIT(3)
+#define HOST_ERROR_INDICATOR UFS_BIT(4)
+#define DEVICE_ERROR_INDICATOR UFS_BIT(5)
+#define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK UFS_MASK(0x7, 8)
+
+/* HCE - Host Controller Enable 34h */
+#define CONTROLLER_ENABLE UFS_BIT(0)
+#define CONTROLLER_DISABLE 0x0
+
+/* UECPA - Host UIC Error Code PHY Adapter Layer 38h */
+#define UIC_PHY_ADAPTER_LAYER_ERROR UFS_BIT(31)
+#define UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK 0x1F
+
+/* UECDL - Host UIC Error Code Data Link Layer 3Ch */
+#define UIC_DATA_LINK_LAYER_ERROR UFS_BIT(31)
+#define UIC_DATA_LINK_LAYER_ERROR_CODE_MASK 0x7FFF
+#define UIC_DATA_LINK_LAYER_ERROR_PA_INIT 0x2000
+
+/* UECN - Host UIC Error Code Network Layer 40h */
+#define UIC_NETWORK_LAYER_ERROR UFS_BIT(31)
+#define UIC_NETWORK_LAYER_ERROR_CODE_MASK 0x7
+
+/* UECT - Host UIC Error Code Transport Layer 44h */
+#define UIC_TRANSPORT_LAYER_ERROR UFS_BIT(31)
+#define UIC_TRANSPORT_LAYER_ERROR_CODE_MASK 0x7F
+
+/* UECDME - Host UIC Error Code DME 48h */
+#define UIC_DME_ERROR UFS_BIT(31)
+#define UIC_DME_ERROR_CODE_MASK 0x1
+
+#define INT_AGGR_TIMEOUT_VAL_MASK 0xFF
+#define INT_AGGR_COUNTER_THRESHOLD_MASK UFS_MASK(0x1F, 8)
+#define INT_AGGR_COUNTER_AND_TIMER_RESET UFS_BIT(16)
+#define INT_AGGR_STATUS_BIT UFS_BIT(20)
+#define INT_AGGR_PARAM_WRITE UFS_BIT(24)
+#define INT_AGGR_ENABLE UFS_BIT(31)
+
+/* UTRLRSR - UTP Transfer Request Run-Stop Register 60h */
+#define UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT UFS_BIT(0)
+
+/* UTMRLRSR - UTP Task Management Request Run-Stop Register 80h */
+#define UTP_TASK_REQ_LIST_RUN_STOP_BIT UFS_BIT(0)
+
+/* UICCMD - UIC Command */
+#define COMMAND_OPCODE_MASK 0xFF
+#define GEN_SELECTOR_INDEX_MASK 0xFFFF
+
+#define MIB_ATTRIBUTE_MASK UFS_MASK(0xFFFF, 16)
+#define RESET_LEVEL 0xFF
+
+#define ATTR_SET_TYPE_MASK UFS_MASK(0xFF, 16)
+#define CONFIG_RESULT_CODE_MASK 0xFF
+#define GENERIC_ERROR_CODE_MASK 0xFF
+
+/* UIC Commands */
+enum {
+ UIC_CMD_DME_GET = 0x01,
+ UIC_CMD_DME_SET = 0x02,
+ UIC_CMD_DME_PEER_GET = 0x03,
+ UIC_CMD_DME_PEER_SET = 0x04,
+ UIC_CMD_DME_POWERON = 0x10,
+ UIC_CMD_DME_POWEROFF = 0x11,
+ UIC_CMD_DME_ENABLE = 0x12,
+ UIC_CMD_DME_RESET = 0x14,
+ UIC_CMD_DME_END_PT_RST = 0x15,
+ UIC_CMD_DME_LINK_STARTUP = 0x16,
+ UIC_CMD_DME_HIBER_ENTER = 0x17,
+ UIC_CMD_DME_HIBER_EXIT = 0x18,
+ UIC_CMD_DME_TEST_MODE = 0x1A,
+};
+
+/* UIC Config result code / Generic error code */
+enum {
+ UIC_CMD_RESULT_SUCCESS = 0x00,
+ UIC_CMD_RESULT_INVALID_ATTR = 0x01,
+ UIC_CMD_RESULT_FAILURE = 0x01,
+ UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02,
+ UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03,
+ UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04,
+ UIC_CMD_RESULT_BAD_INDEX = 0x05,
+ UIC_CMD_RESULT_LOCKED_ATTR = 0x06,
+ UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07,
+ UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08,
+ UIC_CMD_RESULT_BUSY = 0x09,
+ UIC_CMD_RESULT_DME_FAILURE = 0x0A,
+};
+
+#define MASK_UIC_COMMAND_RESULT 0xFF
+
+#define INT_AGGR_COUNTER_THRESHOLD_VALUE (0x1F << 8)
+#define INT_AGGR_TIMEOUT_VALUE (0x02)
+
+/* Interrupt disable masks */
+enum {
+ /* Interrupt disable mask for UFSHCI v1.0 */
+ INTERRUPT_DISABLE_MASK_10 = 0xFFFF,
+
+ /* Interrupt disable mask for UFSHCI v1.1 */
+ INTERRUPT_DISABLE_MASK_11 = 0x0,
+};
+
+/*
+ * Request Descriptor Definitions
+ */
+
+/* Transfer request command type */
+enum {
+ UTP_CMD_TYPE_SCSI = 0x0,
+ UTP_CMD_TYPE_UFS = 0x1,
+ UTP_CMD_TYPE_DEV_MANAGE = 0x2,
+};
+
+enum {
+ UTP_SCSI_COMMAND = 0x00000000,
+ UTP_NATIVE_UFS_COMMAND = 0x10000000,
+ UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000,
+ UTP_REQ_DESC_INT_CMD = 0x01000000,
+};
+
+/* UTP Transfer Request Data Direction (DD) */
+enum {
+ UTP_NO_DATA_TRANSFER = 0x00000000,
+ UTP_HOST_TO_DEVICE = 0x02000000,
+ UTP_DEVICE_TO_HOST = 0x04000000,
+};
+
+/* Overall command status values */
+enum {
+ OCS_SUCCESS = 0x0,
+ OCS_INVALID_CMD_TABLE_ATTR = 0x1,
+ OCS_INVALID_PRDT_ATTR = 0x2,
+ OCS_MISMATCH_DATA_BUF_SIZE = 0x3,
+ OCS_MISMATCH_RESP_UPIU_SIZE = 0x4,
+ OCS_PEER_COMM_FAILURE = 0x5,
+ OCS_ABORTED = 0x6,
+ OCS_FATAL_ERROR = 0x7,
+ OCS_INVALID_COMMAND_STATUS = 0x0F,
+ MASK_OCS = 0x0F,
+};
+
+/**
+ * struct ufshcd_sg_entry - UFSHCI PRD Entry
+ * @base_addr: Lower 32bit physical address DW-0
+ * @upper_addr: Upper 32bit physical address DW-1
+ * @reserved: Reserved for future use DW-2
+ * @size: size of physical segment DW-3
+ */
+struct ufshcd_sg_entry {
+ u32 base_addr;
+ u32 upper_addr;
+ u32 reserved;
+ u32 size;
+};
+
+/**
+ * struct utp_transfer_cmd_desc - UFS Command Descriptor structure
+ * @command_upiu: Command UPIU Frame address
+ * @response_upiu: Response UPIU Frame address
+ * @prd_table: Physical Region Descriptor
+ */
+struct utp_transfer_cmd_desc {
+ u8 command_upiu[ALIGNED_UPIU_SIZE];
+ u8 response_upiu[ALIGNED_UPIU_SIZE];
+ struct ufshcd_sg_entry prd_table[SG_ALL];
+};
+
+/**
+ * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD
+ * @dword0: Descriptor Header DW0
+ * @dword1: Descriptor Header DW1
+ * @dword2: Descriptor Header DW2
+ * @dword3: Descriptor Header DW3
+ */
+struct request_desc_header {
+ u32 dword_0;
+ u32 dword_1;
+ u32 dword_2;
+ u32 dword_3;
+};
+
+/**
+ * struct utp_transfer_req_desc - UTRD structure
+ * @header: UTRD header DW-0 to DW-3
+ * @command_desc_base_addr_lo: UCD base address low DW-4
+ * @command_desc_base_addr_hi: UCD base address high DW-5
+ * @response_upiu_length: response UPIU length DW-6
+ * @response_upiu_offset: response UPIU offset DW-6
+ * @prd_table_length: Physical region descriptor length DW-7
+ * @prd_table_offset: Physical region descriptor offset DW-7
+ */
+struct utp_transfer_req_desc {
+
+ /* DW 0-3 */
+ struct request_desc_header header;
+
+ /* DW 4-5*/
+ u32 command_desc_base_addr_lo;
+ u32 command_desc_base_addr_hi;
+
+ /* DW 6 */
+ u16 response_upiu_length;
+ u16 response_upiu_offset;
+
+ /* DW 7 */
+ u16 prd_table_length;
+ u16 prd_table_offset;
+};
+
+/**
+ * struct utp_task_req_desc - UTMRD structure
+ * @header: UTMRD header DW-0 to DW-3
+ * @task_req_upiu: Pointer to task request UPIU DW-4 to DW-11
+ * @task_rsp_upiu: Pointer to task response UPIU DW12 to DW-19
+ */
+struct utp_task_req_desc {
+
+ /* DW 0-3 */
+ struct request_desc_header header;
+
+ /* DW 4-11 */
+ u32 task_req_upiu[TASK_REQ_UPIU_SIZE_DWORDS];
+
+ /* DW 12-19 */
+ u32 task_rsp_upiu[TASK_RSP_UPIU_SIZE_DWORDS];
+};
+
+#endif /* End of Header */
diff --git a/drivers/scsi/ultrastor.c b/drivers/scsi/ultrastor.c
index 7e22b73..14e0c40 100644
--- a/drivers/scsi/ultrastor.c
+++ b/drivers/scsi/ultrastor.c
@@ -141,7 +141,6 @@
#include <linux/delay.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/dma.h>
#define ULTRASTOR_PRIVATE /* Get the private stuff from ultrastor.h */
diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c
index 7264116..4411d42 100644
--- a/drivers/scsi/vmw_pvscsi.c
+++ b/drivers/scsi/vmw_pvscsi.c
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Maintained by: Alok N Kataria <akataria@vmware.com>
+ * Maintained by: Arvind Kumar <arvindkumar@vmware.com>
*
*/
@@ -1178,11 +1178,67 @@ static int __devinit pvscsi_allocate_sg(struct pvscsi_adapter *adapter)
return 0;
}
+/*
+ * Query the device, fetch the config info and return the
+ * maximum number of targets on the adapter. In case of
+ * failure due to any reason return default i.e. 16.
+ */
+static u32 pvscsi_get_max_targets(struct pvscsi_adapter *adapter)
+{
+ struct PVSCSICmdDescConfigCmd cmd;
+ struct PVSCSIConfigPageHeader *header;
+ struct device *dev;
+ dma_addr_t configPagePA;
+ void *config_page;
+ u32 numPhys = 16;
+
+ dev = pvscsi_dev(adapter);
+ config_page = pci_alloc_consistent(adapter->dev, PAGE_SIZE,
+ &configPagePA);
+ if (!config_page) {
+ dev_warn(dev, "vmw_pvscsi: failed to allocate memory for config page\n");
+ goto exit;
+ }
+ BUG_ON(configPagePA & ~PAGE_MASK);
+
+ /* Fetch config info from the device. */
+ cmd.configPageAddress = ((u64)PVSCSI_CONFIG_CONTROLLER_ADDRESS) << 32;
+ cmd.configPageNum = PVSCSI_CONFIG_PAGE_CONTROLLER;
+ cmd.cmpAddr = configPagePA;
+ cmd._pad = 0;
+
+ /*
+ * Mark the completion page header with error values. If the device
+ * completes the command successfully, it sets the status values to
+ * indicate success.
+ */
+ header = config_page;
+ memset(header, 0, sizeof *header);
+ header->hostStatus = BTSTAT_INVPARAM;
+ header->scsiStatus = SDSTAT_CHECK;
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_CONFIG, &cmd, sizeof cmd);
+
+ if (header->hostStatus == BTSTAT_SUCCESS &&
+ header->scsiStatus == SDSTAT_GOOD) {
+ struct PVSCSIConfigPageController *config;
+
+ config = config_page;
+ numPhys = config->numPhys;
+ } else
+ dev_warn(dev, "vmw_pvscsi: PVSCSI_CMD_CONFIG failed. hostStatus = 0x%x, scsiStatus = 0x%x\n",
+ header->hostStatus, header->scsiStatus);
+ pci_free_consistent(adapter->dev, PAGE_SIZE, config_page, configPagePA);
+exit:
+ return numPhys;
+}
+
static int __devinit pvscsi_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct pvscsi_adapter *adapter;
struct Scsi_Host *host;
+ struct device *dev;
unsigned int i;
unsigned long flags = 0;
int error;
@@ -1272,6 +1328,13 @@ static int __devinit pvscsi_probe(struct pci_dev *pdev,
}
/*
+ * Ask the device for max number of targets.
+ */
+ host->max_id = pvscsi_get_max_targets(adapter);
+ dev = pvscsi_dev(adapter);
+ dev_info(dev, "vmw_pvscsi: host->max_id: %u\n", host->max_id);
+
+ /*
* From this point on we should reset the adapter if anything goes
* wrong.
*/
diff --git a/drivers/scsi/vmw_pvscsi.h b/drivers/scsi/vmw_pvscsi.h
index 62e36e7..3546e86 100644
--- a/drivers/scsi/vmw_pvscsi.h
+++ b/drivers/scsi/vmw_pvscsi.h
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Maintained by: Alok N Kataria <akataria@vmware.com>
+ * Maintained by: Arvind Kumar <arvindkumar@vmware.com>
*
*/
@@ -26,7 +26,7 @@
#include <linux/types.h>
-#define PVSCSI_DRIVER_VERSION_STRING "1.0.1.0-k"
+#define PVSCSI_DRIVER_VERSION_STRING "1.0.2.0-k"
#define PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT 128
@@ -39,28 +39,45 @@
* host adapter status/error codes
*/
enum HostBusAdapterStatus {
- BTSTAT_SUCCESS = 0x00, /* CCB complete normally with no errors */
- BTSTAT_LINKED_COMMAND_COMPLETED = 0x0a,
- BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG = 0x0b,
- BTSTAT_DATA_UNDERRUN = 0x0c,
- BTSTAT_SELTIMEO = 0x11, /* SCSI selection timeout */
- BTSTAT_DATARUN = 0x12, /* data overrun/underrun */
- BTSTAT_BUSFREE = 0x13, /* unexpected bus free */
- BTSTAT_INVPHASE = 0x14, /* invalid bus phase or sequence requested by target */
- BTSTAT_LUNMISMATCH = 0x17, /* linked CCB has different LUN from first CCB */
- BTSTAT_SENSFAILED = 0x1b, /* auto request sense failed */
- BTSTAT_TAGREJECT = 0x1c, /* SCSI II tagged queueing message rejected by target */
- BTSTAT_BADMSG = 0x1d, /* unsupported message received by the host adapter */
- BTSTAT_HAHARDWARE = 0x20, /* host adapter hardware failed */
- BTSTAT_NORESPONSE = 0x21, /* target did not respond to SCSI ATN, sent a SCSI RST */
- BTSTAT_SENTRST = 0x22, /* host adapter asserted a SCSI RST */
- BTSTAT_RECVRST = 0x23, /* other SCSI devices asserted a SCSI RST */
- BTSTAT_DISCONNECT = 0x24, /* target device reconnected improperly (w/o tag) */
- BTSTAT_BUSRESET = 0x25, /* host adapter issued BUS device reset */
- BTSTAT_ABORTQUEUE = 0x26, /* abort queue generated */
- BTSTAT_HASOFTWARE = 0x27, /* host adapter software error */
- BTSTAT_HATIMEOUT = 0x30, /* host adapter hardware timeout error */
- BTSTAT_SCSIPARITY = 0x34, /* SCSI parity error detected */
+ BTSTAT_SUCCESS = 0x00, /* CCB complete normally with no errors */
+ BTSTAT_LINKED_COMMAND_COMPLETED = 0x0a,
+ BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG = 0x0b,
+ BTSTAT_DATA_UNDERRUN = 0x0c,
+ BTSTAT_SELTIMEO = 0x11, /* SCSI selection timeout */
+ BTSTAT_DATARUN = 0x12, /* data overrun/underrun */
+ BTSTAT_BUSFREE = 0x13, /* unexpected bus free */
+ BTSTAT_INVPHASE = 0x14, /* invalid bus phase or sequence
+ * requested by target */
+ BTSTAT_LUNMISMATCH = 0x17, /* linked CCB has different LUN from
+ * first CCB */
+ BTSTAT_INVPARAM = 0x1a, /* invalid parameter in CCB or segment
+ * list */
+ BTSTAT_SENSFAILED = 0x1b, /* auto request sense failed */
+ BTSTAT_TAGREJECT = 0x1c, /* SCSI II tagged queueing message
+ * rejected by target */
+ BTSTAT_BADMSG = 0x1d, /* unsupported message received by the
+ * host adapter */
+ BTSTAT_HAHARDWARE = 0x20, /* host adapter hardware failed */
+ BTSTAT_NORESPONSE = 0x21, /* target did not respond to SCSI ATN,
+ * sent a SCSI RST */
+ BTSTAT_SENTRST = 0x22, /* host adapter asserted a SCSI RST */
+ BTSTAT_RECVRST = 0x23, /* other SCSI devices asserted a SCSI
+ * RST */
+ BTSTAT_DISCONNECT = 0x24, /* target device reconnected improperly
+ * (w/o tag) */
+ BTSTAT_BUSRESET = 0x25, /* host adapter issued BUS device reset */
+ BTSTAT_ABORTQUEUE = 0x26, /* abort queue generated */
+ BTSTAT_HASOFTWARE = 0x27, /* host adapter software error */
+ BTSTAT_HATIMEOUT = 0x30, /* host adapter hardware timeout error */
+ BTSTAT_SCSIPARITY = 0x34, /* SCSI parity error detected */
+};
+
+/*
+ * SCSI device status values.
+ */
+enum ScsiDeviceStatus {
+ SDSTAT_GOOD = 0x00, /* No errors. */
+ SDSTAT_CHECK = 0x02, /* Check condition. */
};
/*
@@ -114,6 +131,29 @@ struct PVSCSICmdDescResetDevice {
} __packed;
/*
+ * Command descriptor for PVSCSI_CMD_CONFIG --
+ */
+
+struct PVSCSICmdDescConfigCmd {
+ u64 cmpAddr;
+ u64 configPageAddress;
+ u32 configPageNum;
+ u32 _pad;
+} __packed;
+
+enum PVSCSIConfigPageType {
+ PVSCSI_CONFIG_PAGE_CONTROLLER = 0x1958,
+ PVSCSI_CONFIG_PAGE_PHY = 0x1959,
+ PVSCSI_CONFIG_PAGE_DEVICE = 0x195a,
+};
+
+enum PVSCSIConfigPageAddressType {
+ PVSCSI_CONFIG_CONTROLLER_ADDRESS = 0x2120,
+ PVSCSI_CONFIG_BUSTARGET_ADDRESS = 0x2121,
+ PVSCSI_CONFIG_PHY_ADDRESS = 0x2122,
+};
+
+/*
* Command descriptor for PVSCSI_CMD_ABORT_CMD --
*
* - currently does not support specifying the LUN.
@@ -332,6 +372,27 @@ struct PVSCSIRingCmpDesc {
u32 _pad[2];
} __packed;
+struct PVSCSIConfigPageHeader {
+ u32 pageNum;
+ u16 numDwords;
+ u16 hostStatus;
+ u16 scsiStatus;
+ u16 reserved[3];
+} __packed;
+
+struct PVSCSIConfigPageController {
+ struct PVSCSIConfigPageHeader header;
+ u64 nodeWWN; /* Device name as defined in the SAS spec. */
+ u16 manufacturer[64];
+ u16 serialNumber[64];
+ u16 opromVersion[32];
+ u16 hwVersion[32];
+ u16 firmwareVersion[32];
+ u32 numPhys;
+ u8 useConsecutivePhyWWNs;
+ u8 reserved[3];
+} __packed;
+
/*
* Interrupt status / IRQ bits.
*/
diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c
index 9ee0afe..d89a5df 100644
--- a/drivers/scsi/wd7000.c
+++ b/drivers/scsi/wd7000.c
@@ -179,7 +179,6 @@
#include <linux/stat.h>
#include <linux/io.h>
-#include <asm/system.h>
#include <asm/dma.h>
#include <scsi/scsi.h>
diff --git a/drivers/sh/intc/balancing.c b/drivers/sh/intc/balancing.c
index cec7a96..bc78080 100644
--- a/drivers/sh/intc/balancing.c
+++ b/drivers/sh/intc/balancing.c
@@ -9,7 +9,7 @@
*/
#include "internals.h"
-static unsigned long dist_handle[NR_IRQS];
+static unsigned long dist_handle[INTC_NR_IRQS];
void intc_balancing_enable(unsigned int irq)
{
diff --git a/drivers/sh/intc/chip.c b/drivers/sh/intc/chip.c
index 7b246ef..012df26 100644
--- a/drivers/sh/intc/chip.c
+++ b/drivers/sh/intc/chip.c
@@ -2,13 +2,14 @@
* IRQ chip definitions for INTC IRQs.
*
* Copyright (C) 2007, 2008 Magnus Damm
- * Copyright (C) 2009, 2010 Paul Mundt
+ * Copyright (C) 2009 - 2012 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/cpumask.h>
+#include <linux/bsearch.h>
#include <linux/io.h>
#include "internals.h"
@@ -58,11 +59,6 @@ static void intc_disable(struct irq_data *data)
}
}
-static int intc_set_wake(struct irq_data *data, unsigned int on)
-{
- return 0; /* allow wakeup, but setup hardware in intc_suspend() */
-}
-
#ifdef CONFIG_SMP
/*
* This is held with the irq desc lock held, so we don't require any
@@ -78,7 +74,7 @@ static int intc_set_affinity(struct irq_data *data,
cpumask_copy(data->affinity, cpumask);
- return 0;
+ return IRQ_SET_MASK_OK_NOCOPY;
}
#endif
@@ -122,28 +118,12 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
unsigned int nr_hp,
unsigned int irq)
{
- int i;
-
- /*
- * this doesn't scale well, but...
- *
- * this function should only be used for cerain uncommon
- * operations such as intc_set_priority() and intc_set_type()
- * and in those rare cases performance doesn't matter that much.
- * keeping the memory footprint low is more important.
- *
- * one rather simple way to speed this up and still keep the
- * memory footprint down is to make sure the array is sorted
- * and then perform a bisect to lookup the irq.
- */
- for (i = 0; i < nr_hp; i++) {
- if ((hp + i)->irq != irq)
- continue;
+ struct intc_handle_int key;
- return hp + i;
- }
+ key.irq = irq;
+ key.handle = 0;
- return NULL;
+ return bsearch(&key, hp, nr_hp, sizeof(*hp), intc_handle_int_cmp);
}
int intc_set_priority(unsigned int irq, unsigned int prio)
@@ -223,10 +203,9 @@ struct irq_chip intc_irq_chip = {
.irq_mask_ack = intc_mask_ack,
.irq_enable = intc_enable,
.irq_disable = intc_disable,
- .irq_shutdown = intc_disable,
.irq_set_type = intc_set_type,
- .irq_set_wake = intc_set_wake,
#ifdef CONFIG_SMP
.irq_set_affinity = intc_set_affinity,
#endif
+ .flags = IRQCHIP_SKIP_SET_WAKE,
};
diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c
index e53e449..7e562cc 100644
--- a/drivers/sh/intc/core.c
+++ b/drivers/sh/intc/core.c
@@ -2,7 +2,7 @@
* Shared interrupt handling code for IPR and INTC2 types of IRQs.
*
* Copyright (C) 2007, 2008 Magnus Damm
- * Copyright (C) 2009, 2010 Paul Mundt
+ * Copyright (C) 2009 - 2012 Paul Mundt
*
* Based on intc2.c and ipr.c
*
@@ -31,18 +31,19 @@
#include <linux/spinlock.h>
#include <linux/radix-tree.h>
#include <linux/export.h>
+#include <linux/sort.h>
#include "internals.h"
LIST_HEAD(intc_list);
DEFINE_RAW_SPINLOCK(intc_big_lock);
-unsigned int nr_intc_controllers;
+static unsigned int nr_intc_controllers;
/*
* Default priority level
* - this needs to be at least 2 for 5-bit priorities on 7780
*/
static unsigned int default_prio_level = 2; /* 2 - 16 */
-static unsigned int intc_prio_level[NR_IRQS]; /* for now */
+static unsigned int intc_prio_level[INTC_NR_IRQS]; /* for now */
unsigned int intc_get_dfl_prio_level(void)
{
@@ -267,6 +268,9 @@ int __init register_intc_controller(struct intc_desc *desc)
k += save_reg(d, k, hw->prio_regs[i].set_reg, smp);
k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp);
}
+
+ sort(d->prio, hw->nr_prio_regs, sizeof(*d->prio),
+ intc_handle_int_cmp, NULL);
}
if (hw->sense_regs) {
@@ -277,6 +281,9 @@ int __init register_intc_controller(struct intc_desc *desc)
for (i = 0; i < hw->nr_sense_regs; i++)
k += save_reg(d, k, hw->sense_regs[i].reg, 0);
+
+ sort(d->sense, hw->nr_sense_regs, sizeof(*d->sense),
+ intc_handle_int_cmp, NULL);
}
if (hw->subgroups)
diff --git a/drivers/sh/intc/handle.c b/drivers/sh/intc/handle.c
index 057ce56..7863a44 100644
--- a/drivers/sh/intc/handle.c
+++ b/drivers/sh/intc/handle.c
@@ -13,7 +13,7 @@
#include <linux/spinlock.h>
#include "internals.h"
-static unsigned long ack_handle[NR_IRQS];
+static unsigned long ack_handle[INTC_NR_IRQS];
static intc_enum __init intc_grp_id(struct intc_desc *desc,
intc_enum enum_id)
@@ -172,9 +172,8 @@ intc_get_prio_handle(struct intc_desc *desc, struct intc_desc_int *d,
return 0;
}
-static unsigned int __init intc_ack_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id)
+static unsigned int intc_ack_data(struct intc_desc *desc,
+ struct intc_desc_int *d, intc_enum enum_id)
{
struct intc_mask_reg *mr = desc->hw.ack_regs;
unsigned int i, j, fn, mode;
diff --git a/drivers/sh/intc/internals.h b/drivers/sh/intc/internals.h
index b0e9155..f034a97 100644
--- a/drivers/sh/intc/internals.h
+++ b/drivers/sh/intc/internals.h
@@ -108,6 +108,14 @@ static inline void activate_irq(int irq)
#endif
}
+static inline int intc_handle_int_cmp(const void *a, const void *b)
+{
+ const struct intc_handle_int *_a = a;
+ const struct intc_handle_int *_b = b;
+
+ return _a->irq - _b->irq;
+}
+
/* access.c */
extern unsigned long
(*intc_reg_fns[])(unsigned long addr, unsigned long h, unsigned long data);
@@ -157,7 +165,6 @@ void _intc_enable(struct irq_data *data, unsigned long handle);
/* core.c */
extern struct list_head intc_list;
extern raw_spinlock_t intc_big_lock;
-extern unsigned int nr_intc_controllers;
extern struct bus_type intc_subsys;
unsigned int intc_get_dfl_prio_level(void);
diff --git a/drivers/sh/intc/virq.c b/drivers/sh/intc/virq.c
index c7ec49f..93cec21 100644
--- a/drivers/sh/intc/virq.c
+++ b/drivers/sh/intc/virq.c
@@ -17,7 +17,7 @@
#include <linux/export.h>
#include "internals.h"
-static struct intc_map_entry intc_irq_xlate[NR_IRQS];
+static struct intc_map_entry intc_irq_xlate[INTC_NR_IRQS];
struct intc_virq_list {
unsigned int irq;
diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c
index 8418eb0..b9f0192 100644
--- a/drivers/spi/spi-dw-mid.c
+++ b/drivers/spi/spi-dw-mid.c
@@ -22,6 +22,7 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
+#include <linux/types.h>
#include "spi-dw.h"
@@ -136,6 +137,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
txconf.dst_maxburst = LNW_DMA_MSIZE_16;
txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ txconf.device_fc = false;
txchan->device->device_control(txchan, DMA_SLAVE_CONFIG,
(unsigned long) &txconf);
@@ -144,7 +146,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
dws->tx_sgl.dma_address = dws->tx_dma;
dws->tx_sgl.length = dws->len;
- txdesc = txchan->device->device_prep_slave_sg(txchan,
+ txdesc = dmaengine_prep_slave_sg(txchan,
&dws->tx_sgl,
1,
DMA_MEM_TO_DEV,
@@ -158,6 +160,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
rxconf.src_maxburst = LNW_DMA_MSIZE_16;
rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ rxconf.device_fc = false;
rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG,
(unsigned long) &rxconf);
@@ -166,7 +169,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
dws->rx_sgl.dma_address = dws->rx_dma;
dws->rx_sgl.length = dws->len;
- rxdesc = rxchan->device->device_prep_slave_sg(rxchan,
+ rxdesc = dmaengine_prep_slave_sg(rxchan,
&dws->rx_sgl,
1,
DMA_DEV_TO_MEM,
diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c
index d46e55c..6db2887 100644
--- a/drivers/spi/spi-ep93xx.c
+++ b/drivers/spi/spi-ep93xx.c
@@ -633,8 +633,8 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir)
if (!nents)
return ERR_PTR(-ENOMEM);
- txd = chan->device->device_prep_slave_sg(chan, sgt->sgl, nents,
- slave_dirn, DMA_CTRL_ACK);
+ txd = dmaengine_prep_slave_sg(chan, sgt->sgl, nents,
+ slave_dirn, DMA_CTRL_ACK);
if (!txd) {
dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
return ERR_PTR(-ENOMEM);
diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c
index 610f739..9b0d716 100644
--- a/drivers/spi/spi-omap-uwire.c
+++ b/drivers/spi/spi-omap-uwire.c
@@ -47,7 +47,6 @@
#include <linux/spi/spi_bitbang.h>
#include <linux/module.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <asm/io.h>
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index 13448c8..e496f79 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -359,11 +359,6 @@ static int orion_spi_setup(struct spi_device *spi)
orion_spi = spi_master_get_devdata(spi->master);
- /* Fix ac timing if required. */
- if (orion_spi->spi_info->enable_clock_fix)
- orion_spi_setbits(orion_spi, ORION_SPI_IF_CONFIG_REG,
- (1 << 14));
-
if ((spi->max_speed_hz == 0)
|| (spi->max_speed_hz > orion_spi->max_speed))
spi->max_speed_hz = orion_spi->max_speed;
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index dc8485d..96f0da6 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -880,10 +880,12 @@ static int configure_dma(struct pl022 *pl022)
struct dma_slave_config rx_conf = {
.src_addr = SSP_DR(pl022->phybase),
.direction = DMA_DEV_TO_MEM,
+ .device_fc = false,
};
struct dma_slave_config tx_conf = {
.dst_addr = SSP_DR(pl022->phybase),
.direction = DMA_MEM_TO_DEV,
+ .device_fc = false,
};
unsigned int pages;
int ret;
@@ -1017,7 +1019,7 @@ static int configure_dma(struct pl022 *pl022)
goto err_tx_sgmap;
/* Send both scatterlists */
- rxdesc = rxchan->device->device_prep_slave_sg(rxchan,
+ rxdesc = dmaengine_prep_slave_sg(rxchan,
pl022->sgt_rx.sgl,
rx_sglen,
DMA_DEV_TO_MEM,
@@ -1025,7 +1027,7 @@ static int configure_dma(struct pl022 *pl022)
if (!rxdesc)
goto err_rxdesc;
- txdesc = txchan->device->device_prep_slave_sg(txchan,
+ txdesc = dmaengine_prep_slave_sg(txchan,
pl022->sgt_tx.sgl,
tx_sglen,
DMA_MEM_TO_DEV,
diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c
index 5c6fa5e..ec47d3b 100644
--- a/drivers/spi/spi-topcliff-pch.c
+++ b/drivers/spi/spi-topcliff-pch.c
@@ -1099,7 +1099,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
sg_dma_address(sg) = dma->rx_buf_dma + sg->offset;
}
sg = dma->sg_rx_p;
- desc_rx = dma->chan_rx->device->device_prep_slave_sg(dma->chan_rx, sg,
+ desc_rx = dmaengine_prep_slave_sg(dma->chan_rx, sg,
num, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_rx) {
@@ -1158,7 +1158,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
sg_dma_address(sg) = dma->tx_buf_dma + sg->offset;
}
sg = dma->sg_tx_p;
- desc_tx = dma->chan_tx->device->device_prep_slave_sg(dma->chan_tx,
+ desc_tx = dmaengine_prep_slave_sg(dma->chan_tx,
sg, num, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_tx) {
diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c
index 59e0953..c283212 100644
--- a/drivers/staging/android/binder.c
+++ b/drivers/staging/android/binder.c
@@ -381,8 +381,7 @@ int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
repeat:
fdt = files_fdtable(files);
- fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds,
- files->next_fd);
+ fd = find_next_zero_bit(fdt->open_fds, fdt->max_fds, files->next_fd);
/*
* N.B. For clone tasks sharing a files structure, this test
@@ -410,11 +409,11 @@ repeat:
goto repeat;
}
- FD_SET(fd, fdt->open_fds);
+ __set_open_fd(fd, fdt);
if (flags & O_CLOEXEC)
- FD_SET(fd, fdt->close_on_exec);
+ __set_close_on_exec(fd, fdt);
else
- FD_CLR(fd, fdt->close_on_exec);
+ __clear_close_on_exec(fd, fdt);
files->next_fd = fd + 1;
#if 1
/* Sanity check */
@@ -455,7 +454,7 @@ static void task_fd_install(
static void __put_unused_fd(struct files_struct *files, unsigned int fd)
{
struct fdtable *fdt = files_fdtable(files);
- __FD_CLR(fd, fdt->open_fds);
+ __clear_open_fd(fd, fdt);
if (fd < files->next_fd)
files->next_fd = fd;
}
@@ -481,7 +480,7 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd)
if (!filp)
goto out_unlock;
rcu_assign_pointer(fdt->fd[fd], NULL);
- FD_CLR(fd, fdt->close_on_exec);
+ __clear_close_on_exec(fd, fdt);
__put_unused_fd(files, fd);
spin_unlock(&files->file_lock);
retval = filp_close(filp, files);
diff --git a/drivers/staging/asus_oled/README b/drivers/staging/asus_oled/README
index 0d82a6d..2d72123 100644
--- a/drivers/staging/asus_oled/README
+++ b/drivers/staging/asus_oled/README
@@ -52,7 +52,7 @@ Configuration
There is only one option: start_off.
You can use it by: 'modprobe asus_oled start_off=1', or by adding this
- line to /etc/modprobe.conf:
+ line to /etc/modprobe.d/asus_oled.conf:
options asus_oled start_off=1
With this option provided, asus_oled driver will switch off the display
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c
index db1fd63..bf185e2 100644
--- a/drivers/staging/comedi/drivers.c
+++ b/drivers/staging/comedi/drivers.c
@@ -42,7 +42,6 @@
#include <linux/cdev.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
-#include <asm/system.h>
#include "comedidev.h"
#include "internal.h"
diff --git a/drivers/staging/comedi/drivers/cb_pcidas64.c b/drivers/staging/comedi/drivers/cb_pcidas64.c
index c9e8c47..915157d 100644
--- a/drivers/staging/comedi/drivers/cb_pcidas64.c
+++ b/drivers/staging/comedi/drivers/cb_pcidas64.c
@@ -86,7 +86,6 @@ TODO:
#include "../comedidev.h"
#include <linux/delay.h>
#include <linux/interrupt.h>
-#include <asm/system.h>
#include "comedi_pci.h"
#include "8253.h"
diff --git a/drivers/staging/comedi/drivers/mite.c b/drivers/staging/comedi/drivers/mite.c
index fd274e9..13e9c80 100644
--- a/drivers/staging/comedi/drivers/mite.c
+++ b/drivers/staging/comedi/drivers/mite.c
@@ -55,7 +55,6 @@
#include "comedi_pci.h"
#include "../comedidev.h"
-#include <asm/system.h>
#define PCI_MITE_SIZE 4096
#define PCI_DAQ_SIZE 4096
diff --git a/drivers/staging/crystalhd/bc_dts_defs.h b/drivers/staging/crystalhd/bc_dts_defs.h
index 8cd51a7..647e116 100644
--- a/drivers/staging/crystalhd/bc_dts_defs.h
+++ b/drivers/staging/crystalhd/bc_dts_defs.h
@@ -26,6 +26,8 @@
#ifndef _BC_DTS_DEFS_H_
#define _BC_DTS_DEFS_H_
+#include <linux/types.h>
+
/* BIT Mask */
#define BC_BIT(_x) (1 << (_x))
diff --git a/drivers/staging/crystalhd/crystalhd.h b/drivers/staging/crystalhd/crystalhd.h
index 3f4d795..b3a550b 100644
--- a/drivers/staging/crystalhd/crystalhd.h
+++ b/drivers/staging/crystalhd/crystalhd.h
@@ -1,7 +1,6 @@
#ifndef _CRYSTALHD_H_
#define _CRYSTALHD_H_
-#include <asm/system.h>
#include "bc_dts_defs.h"
#include "crystalhd_misc.h"
#include "bc_dts_glob_lnx.h"
diff --git a/drivers/staging/crystalhd/crystalhd_lnx.h b/drivers/staging/crystalhd/crystalhd_lnx.h
index a81f929..a9e3633 100644
--- a/drivers/staging/crystalhd/crystalhd_lnx.h
+++ b/drivers/staging/crystalhd/crystalhd_lnx.h
@@ -45,7 +45,6 @@
#include <linux/io.h>
#include <asm/irq.h>
#include <asm/pgtable.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include "crystalhd.h"
diff --git a/drivers/staging/crystalhd/crystalhd_misc.h b/drivers/staging/crystalhd/crystalhd_misc.h
index 84c8793..8cdaa7a 100644
--- a/drivers/staging/crystalhd/crystalhd_misc.h
+++ b/drivers/staging/crystalhd/crystalhd_misc.h
@@ -37,6 +37,7 @@
#include <linux/ioctl.h>
#include <linux/dma-mapping.h>
#include <linux/sched.h>
+#include "bc_dts_glob_lnx.h"
/* Global log level variable defined in crystal_misc.c file */
extern uint32_t g_linklog_level;
diff --git a/drivers/staging/et131x/et131x.c b/drivers/staging/et131x/et131x.c
index 3f919ba..886f565 100644
--- a/drivers/staging/et131x/et131x.c
+++ b/drivers/staging/et131x/et131x.c
@@ -70,7 +70,6 @@
#include <linux/delay.h>
#include <linux/bitops.h>
#include <linux/io.h>
-#include <asm/system.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c
index 7569aa0..c4a8a0a 100644
--- a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c
+++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c
@@ -29,7 +29,6 @@
#include <linux/interrupt.h>
#include <linux/in.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/bitops.h>
#include <linux/netdevice.h>
diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c
index 6c9279a..ece2dd1 100644
--- a/drivers/staging/media/go7007/go7007-driver.c
+++ b/drivers/staging/media/go7007/go7007-driver.c
@@ -30,7 +30,6 @@
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
-#include <asm/system.h>
#include <linux/videodev2.h>
#include <media/tuner.h>
#include <media/v4l2-common.h>
diff --git a/drivers/staging/media/go7007/go7007-i2c.c b/drivers/staging/media/go7007/go7007-i2c.c
index b8cfa1a..6bc82aa 100644
--- a/drivers/staging/media/go7007/go7007-i2c.c
+++ b/drivers/staging/media/go7007/go7007-i2c.c
@@ -26,7 +26,6 @@
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#include "go7007-priv.h"
#include "wis-i2c.h"
diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c
index f916586..3ef4cd8 100644
--- a/drivers/staging/media/go7007/go7007-v4l2.c
+++ b/drivers/staging/media/go7007/go7007-v4l2.c
@@ -34,7 +34,6 @@
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#include "go7007.h"
#include "go7007-priv.h"
diff --git a/drivers/staging/media/go7007/snd-go7007.c b/drivers/staging/media/go7007/snd-go7007.c
index d071c83..5af29ff 100644
--- a/drivers/staging/media/go7007/snd-go7007.c
+++ b/drivers/staging/media/go7007/snd-go7007.c
@@ -29,7 +29,6 @@
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
-#include <asm/system.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c
index 97352cf..3295ea6 100644
--- a/drivers/staging/media/lirc/lirc_serial.c
+++ b/drivers/staging/media/lirc/lirc_serial.c
@@ -66,7 +66,6 @@
#include <linux/poll.h>
#include <linux/platform_device.h>
-#include <asm/system.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/fcntl.h>
diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c
index c94382b..945d962 100644
--- a/drivers/staging/media/lirc/lirc_sir.c
+++ b/drivers/staging/media/lirc/lirc_sir.c
@@ -49,7 +49,6 @@
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/poll.h>
-#include <asm/system.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <linux/fcntl.h>
diff --git a/drivers/staging/mei/wd.c b/drivers/staging/mei/wd.c
index a6910da..cf4c29d 100644
--- a/drivers/staging/mei/wd.c
+++ b/drivers/staging/mei/wd.c
@@ -323,6 +323,7 @@ static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, unsigned int t
mutex_lock(&dev->device_lock);
dev->wd_timeout = timeout;
+ wd_dev->timeout = timeout;
mei_wd_set_start_timeout(dev, dev->wd_timeout);
mutex_unlock(&dev->device_lock);
diff --git a/drivers/staging/panel/panel.c b/drivers/staging/panel/panel.c
index 4683d5f..6183573 100644
--- a/drivers/staging/panel/panel.c
+++ b/drivers/staging/panel/panel.c
@@ -58,7 +58,6 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#define LCD_MINOR 156
#define KEYPAD_MINOR 185
diff --git a/drivers/staging/sbe-2t3e3/io.c b/drivers/staging/sbe-2t3e3/io.c
index b458ff0..9a50bcc 100644
--- a/drivers/staging/sbe-2t3e3/io.c
+++ b/drivers/staging/sbe-2t3e3/io.c
@@ -11,7 +11,6 @@
*/
#include <linux/ip.h>
-#include <asm/system.h>
#include "2t3e3.h"
#include "ctrl.h"
diff --git a/drivers/staging/ste_rmi4/Makefile b/drivers/staging/ste_rmi4/Makefile
index 176f469..e4c0335 100644
--- a/drivers/staging/ste_rmi4/Makefile
+++ b/drivers/staging/ste_rmi4/Makefile
@@ -2,4 +2,4 @@
# Makefile for the RMI4 touchscreen driver.
#
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += synaptics_i2c_rmi4.o
-obj-$(CONFIG_MACH_U8500) += board-mop500-u8500uib-rmi4.o
+obj-$(CONFIG_MACH_MOP500) += board-mop500-u8500uib-rmi4.o
diff --git a/drivers/staging/telephony/phonedev.c b/drivers/staging/telephony/phonedev.c
index 1915af2..1dd0b67 100644
--- a/drivers/staging/telephony/phonedev.c
+++ b/drivers/staging/telephony/phonedev.c
@@ -24,7 +24,6 @@
#include <linux/phonedev.h>
#include <linux/init.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#include <linux/kmod.h>
#include <linux/sem.h>
diff --git a/drivers/staging/tidspbridge/include/dspbridge/host_os.h b/drivers/staging/tidspbridge/include/dspbridge/host_os.h
index a2f31c6..ed00d3d 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/host_os.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/host_os.h
@@ -17,7 +17,6 @@
#ifndef _HOST_OS_H_
#define _HOST_OS_H_
-#include <asm/system.h>
#include <linux/atomic.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
diff --git a/drivers/staging/wlags49_h2/wl_cs.c b/drivers/staging/wlags49_h2/wl_cs.c
index a2cbb29..7084f41 100644
--- a/drivers/staging/wlags49_h2/wl_cs.c
+++ b/drivers/staging/wlags49_h2/wl_cs.c
@@ -74,7 +74,6 @@
#include <linux/in.h>
#include <linux/delay.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/bitops.h>
#include <linux/netdevice.h>
diff --git a/drivers/staging/wlags49_h2/wl_main.c b/drivers/staging/wlags49_h2/wl_main.c
index dab603e..d5bf0a7 100644
--- a/drivers/staging/wlags49_h2/wl_main.c
+++ b/drivers/staging/wlags49_h2/wl_main.c
@@ -86,8 +86,7 @@
// #include <linux/in.h>
// #include <linux/delay.h>
// #include <asm/io.h>
-// #include <asm/system.h>
-// #include <asm/bitops.h>
+// // #include <asm/bitops.h>
#include <linux/unistd.h>
#include <asm/uaccess.h>
diff --git a/drivers/staging/wlags49_h2/wl_netdev.c b/drivers/staging/wlags49_h2/wl_netdev.c
index 9c16f54..90820ff 100644
--- a/drivers/staging/wlags49_h2/wl_netdev.c
+++ b/drivers/staging/wlags49_h2/wl_netdev.c
@@ -79,8 +79,7 @@
// #include <linux/delay.h>
// #include <linux/skbuff.h>
// #include <asm/io.h>
-// #include <asm/system.h>
-// #include <asm/bitops.h>
+// // #include <asm/bitops.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
diff --git a/drivers/staging/wlags49_h2/wl_pci.c b/drivers/staging/wlags49_h2/wl_pci.c
index 2bd9b84..3df990c 100644
--- a/drivers/staging/wlags49_h2/wl_pci.c
+++ b/drivers/staging/wlags49_h2/wl_pci.c
@@ -77,7 +77,6 @@
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/delay.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/bitops.h>
diff --git a/drivers/staging/wlags49_h2/wl_util.c b/drivers/staging/wlags49_h2/wl_util.c
index b748a3f..f104e6f 100644
--- a/drivers/staging/wlags49_h2/wl_util.c
+++ b/drivers/staging/wlags49_h2/wl_util.c
@@ -73,8 +73,7 @@
// #include <linux/in.h>
// #include <linux/delay.h>
// #include <asm/io.h>
-// #include <asm/system.h>
-// #include <asm/bitops.h>
+// // #include <asm/bitops.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index f7f71b2..514a691 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -18,3 +18,11 @@ config THERMAL_HWMON
depends on THERMAL
depends on HWMON=y || HWMON=THERMAL
default y
+
+config SPEAR_THERMAL
+ bool "SPEAr thermal sensor driver"
+ depends on THERMAL
+ depends on PLAT_SPEAR
+ help
+ Enable this to plug the SPEAr thermal sensor driver into the Linux
+ thermal framework
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 31108a0..a9fff0b 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_THERMAL) += thermal_sys.o
+obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o \ No newline at end of file
diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c
new file mode 100644
index 0000000..c2e32df
--- /dev/null
+++ b/drivers/thermal/spear_thermal.c
@@ -0,0 +1,206 @@
+/*
+ * SPEAr thermal driver.
+ *
+ * Copyright (C) 2011-2012 ST Microelectronics
+ * Author: Vincenzo Frascino <vincenzo.frascino@st.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/spear_thermal.h>
+#include <linux/thermal.h>
+
+#define MD_FACTOR 1000
+
+/* SPEAr Thermal Sensor Dev Structure */
+struct spear_thermal_dev {
+ /* pointer to base address of the thermal sensor */
+ void __iomem *thermal_base;
+ /* clk structure */
+ struct clk *clk;
+ /* pointer to thermal flags */
+ unsigned int flags;
+};
+
+static inline int thermal_get_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ struct spear_thermal_dev *stdev = thermal->devdata;
+
+ /*
+ * Data are ready to be read after 628 usec from POWERDOWN signal
+ * (PDN) = 1
+ */
+ *temp = (readl_relaxed(stdev->thermal_base) & 0x7F) * MD_FACTOR;
+ return 0;
+}
+
+static struct thermal_zone_device_ops ops = {
+ .get_temp = thermal_get_temp,
+};
+
+#ifdef CONFIG_PM
+static int spear_thermal_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev);
+ struct spear_thermal_dev *stdev = spear_thermal->devdata;
+ unsigned int actual_mask = 0;
+
+ /* Disable SPEAr Thermal Sensor */
+ actual_mask = readl_relaxed(stdev->thermal_base);
+ writel_relaxed(actual_mask & ~stdev->flags, stdev->thermal_base);
+
+ clk_disable(stdev->clk);
+ dev_info(dev, "Suspended.\n");
+
+ return 0;
+}
+
+static int spear_thermal_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev);
+ struct spear_thermal_dev *stdev = spear_thermal->devdata;
+ unsigned int actual_mask = 0;
+ int ret = 0;
+
+ ret = clk_enable(stdev->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't enable clock\n");
+ return ret;
+ }
+
+ /* Enable SPEAr Thermal Sensor */
+ actual_mask = readl_relaxed(stdev->thermal_base);
+ writel_relaxed(actual_mask | stdev->flags, stdev->thermal_base);
+
+ dev_info(dev, "Resumed.\n");
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(spear_thermal_pm_ops, spear_thermal_suspend,
+ spear_thermal_resume);
+
+static int spear_thermal_probe(struct platform_device *pdev)
+{
+ struct thermal_zone_device *spear_thermal = NULL;
+ struct spear_thermal_dev *stdev;
+ struct spear_thermal_pdata *pdata;
+ int ret = 0;
+ struct resource *stres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!stres) {
+ dev_err(&pdev->dev, "memory resource missing\n");
+ return -ENODEV;
+ }
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "platform data is NULL\n");
+ return -EINVAL;
+ }
+
+ stdev = devm_kzalloc(&pdev->dev, sizeof(*stdev), GFP_KERNEL);
+ if (!stdev) {
+ dev_err(&pdev->dev, "kzalloc fail\n");
+ return -ENOMEM;
+ }
+
+ /* Enable thermal sensor */
+ stdev->thermal_base = devm_ioremap(&pdev->dev, stres->start,
+ resource_size(stres));
+ if (!stdev->thermal_base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ return -ENOMEM;
+ }
+
+ stdev->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(stdev->clk)) {
+ dev_err(&pdev->dev, "Can't get clock\n");
+ return PTR_ERR(stdev->clk);
+ }
+
+ ret = clk_enable(stdev->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't enable clock\n");
+ goto put_clk;
+ }
+
+ stdev->flags = pdata->thermal_flags;
+ writel_relaxed(stdev->flags, stdev->thermal_base);
+
+ spear_thermal = thermal_zone_device_register("spear_thermal", 0,
+ stdev, &ops, 0, 0, 0, 0);
+ if (IS_ERR(spear_thermal)) {
+ dev_err(&pdev->dev, "thermal zone device is NULL\n");
+ ret = PTR_ERR(spear_thermal);
+ goto disable_clk;
+ }
+
+ platform_set_drvdata(pdev, spear_thermal);
+
+ dev_info(&spear_thermal->device, "Thermal Sensor Loaded at: 0x%p.\n",
+ stdev->thermal_base);
+
+ return 0;
+
+disable_clk:
+ clk_disable(stdev->clk);
+put_clk:
+ clk_put(stdev->clk);
+
+ return ret;
+}
+
+static int spear_thermal_exit(struct platform_device *pdev)
+{
+ unsigned int actual_mask = 0;
+ struct thermal_zone_device *spear_thermal = platform_get_drvdata(pdev);
+ struct spear_thermal_dev *stdev = spear_thermal->devdata;
+
+ thermal_zone_device_unregister(spear_thermal);
+ platform_set_drvdata(pdev, NULL);
+
+ /* Disable SPEAr Thermal Sensor */
+ actual_mask = readl_relaxed(stdev->thermal_base);
+ writel_relaxed(actual_mask & ~stdev->flags, stdev->thermal_base);
+
+ clk_disable(stdev->clk);
+ clk_put(stdev->clk);
+
+ return 0;
+}
+
+static struct platform_driver spear_thermal_driver = {
+ .probe = spear_thermal_probe,
+ .remove = spear_thermal_exit,
+ .driver = {
+ .name = "spear_thermal",
+ .owner = THIS_MODULE,
+ .pm = &spear_thermal_pm_ops,
+ },
+};
+
+module_platform_driver(spear_thermal_driver);
+
+MODULE_AUTHOR("Vincenzo Frascino <vincenzo.frascino@st.com>");
+MODULE_DESCRIPTION("SPEAr thermal driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 220ce7e..022bacb 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -23,6 +23,8 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -39,8 +41,6 @@ MODULE_AUTHOR("Zhang Rui");
MODULE_DESCRIPTION("Generic thermal management sysfs support");
MODULE_LICENSE("GPL");
-#define PREFIX "Thermal: "
-
struct thermal_cooling_device_instance {
int id;
char name[THERMAL_NAME_LENGTH];
@@ -60,13 +60,11 @@ static LIST_HEAD(thermal_tz_list);
static LIST_HEAD(thermal_cdev_list);
static DEFINE_MUTEX(thermal_list_lock);
-static unsigned int thermal_event_seqnum;
-
static int get_idr(struct idr *idr, struct mutex *lock, int *id)
{
int err;
- again:
+again:
if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
return -ENOMEM;
@@ -152,9 +150,9 @@ mode_store(struct device *dev, struct device_attribute *attr,
if (!tz->ops->set_mode)
return -EPERM;
- if (!strncmp(buf, "enabled", sizeof("enabled")))
+ if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
- else if (!strncmp(buf, "disabled", sizeof("disabled")))
+ else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
else
result = -EINVAL;
@@ -283,8 +281,7 @@ passive_show(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(type, 0444, type_show, NULL);
static DEVICE_ATTR(temp, 0444, temp_show, NULL);
static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
-static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, \
- passive_store);
+static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store);
static struct device_attribute trip_point_attrs[] = {
__ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),
@@ -313,22 +310,6 @@ static struct device_attribute trip_point_attrs[] = {
__ATTR(trip_point_11_temp, 0444, trip_point_temp_show, NULL),
};
-#define TRIP_POINT_ATTR_ADD(_dev, _index, result) \
-do { \
- result = device_create_file(_dev, \
- &trip_point_attrs[_index * 2]); \
- if (result) \
- break; \
- result = device_create_file(_dev, \
- &trip_point_attrs[_index * 2 + 1]); \
-} while (0)
-
-#define TRIP_POINT_ATTR_REMOVE(_dev, _index) \
-do { \
- device_remove_file(_dev, &trip_point_attrs[_index * 2]); \
- device_remove_file(_dev, &trip_point_attrs[_index * 2 + 1]); \
-} while (0)
-
/* sys I/F for cooling device */
#define to_cooling_device(_dev) \
container_of(_dev, struct thermal_cooling_device, device)
@@ -835,15 +816,14 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
return 0;
device_remove_file(&tz->device, &dev->attr);
- remove_symbol_link:
+remove_symbol_link:
sysfs_remove_link(&tz->device.kobj, dev->name);
- release_idr:
+release_idr:
release_idr(&tz->idr, &tz->lock, dev->id);
- free_mem:
+free_mem:
kfree(dev);
return result;
}
-
EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
/**
@@ -873,14 +853,13 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
return -ENODEV;
- unbind:
+unbind:
device_remove_file(&tz->device, &pos->attr);
sysfs_remove_link(&tz->device.kobj, pos->name);
release_idr(&tz->idr, &tz->lock, pos->id);
kfree(pos);
return 0;
}
-
EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
static void thermal_release(struct device *dev)
@@ -888,7 +867,8 @@ static void thermal_release(struct device *dev)
struct thermal_zone_device *tz;
struct thermal_cooling_device *cdev;
- if (!strncmp(dev_name(dev), "thermal_zone", sizeof "thermal_zone" - 1)) {
+ if (!strncmp(dev_name(dev), "thermal_zone",
+ sizeof("thermal_zone") - 1)) {
tz = to_thermal_zone(dev);
kfree(tz);
} else {
@@ -908,8 +888,9 @@ static struct class thermal_class = {
* @devdata: device private data.
* @ops: standard thermal cooling devices callbacks.
*/
-struct thermal_cooling_device *thermal_cooling_device_register(
- char *type, void *devdata, const struct thermal_cooling_device_ops *ops)
+struct thermal_cooling_device *
+thermal_cooling_device_register(char *type, void *devdata,
+ const struct thermal_cooling_device_ops *ops)
{
struct thermal_cooling_device *cdev;
struct thermal_zone_device *pos;
@@ -974,12 +955,11 @@ struct thermal_cooling_device *thermal_cooling_device_register(
if (!result)
return cdev;
- unregister:
+unregister:
release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
device_unregister(&cdev->device);
return ERR_PTR(result);
}
-
EXPORT_SYMBOL(thermal_cooling_device_register);
/**
@@ -1024,7 +1004,6 @@ void thermal_cooling_device_unregister(struct
device_unregister(&cdev->device);
return;
}
-
EXPORT_SYMBOL(thermal_cooling_device_unregister);
/**
@@ -1044,8 +1023,7 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
if (tz->ops->get_temp(tz, &temp)) {
/* get_temp failed - retry it later */
- printk(KERN_WARNING PREFIX "failed to read out thermal zone "
- "%d\n", tz->id);
+ pr_warn("failed to read out thermal zone %d\n", tz->id);
goto leave;
}
@@ -1060,9 +1038,8 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
ret = tz->ops->notify(tz, count,
trip_type);
if (!ret) {
- printk(KERN_EMERG
- "Critical temperature reached (%ld C), shutting down.\n",
- temp/1000);
+ pr_emerg("Critical temperature reached (%ld C), shutting down\n",
+ temp/1000);
orderly_poweroff(true);
}
}
@@ -1100,7 +1077,7 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
tz->last_temperature = temp;
- leave:
+leave:
if (tz->passive)
thermal_zone_device_set_polling(tz, tz->passive_delay);
else if (tz->polling_delay)
@@ -1199,7 +1176,12 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
}
for (count = 0; count < trips; count++) {
- TRIP_POINT_ATTR_ADD(&tz->device, count, result);
+ result = device_create_file(&tz->device,
+ &trip_point_attrs[count * 2]);
+ if (result)
+ break;
+ result = device_create_file(&tz->device,
+ &trip_point_attrs[count * 2 + 1]);
if (result)
goto unregister;
tz->ops->get_trip_type(tz, count, &trip_type);
@@ -1235,12 +1217,11 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
if (!result)
return tz;
- unregister:
+unregister:
release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
device_unregister(&tz->device);
return ERR_PTR(result);
}
-
EXPORT_SYMBOL(thermal_zone_device_register);
/**
@@ -1279,9 +1260,12 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
if (tz->ops->get_mode)
device_remove_file(&tz->device, &dev_attr_mode);
- for (count = 0; count < tz->trips; count++)
- TRIP_POINT_ATTR_REMOVE(&tz->device, count);
-
+ for (count = 0; count < tz->trips; count++) {
+ device_remove_file(&tz->device,
+ &trip_point_attrs[count * 2]);
+ device_remove_file(&tz->device,
+ &trip_point_attrs[count * 2 + 1]);
+ }
thermal_remove_hwmon_sysfs(tz);
release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
idr_destroy(&tz->idr);
@@ -1289,7 +1273,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
device_unregister(&tz->device);
return;
}
-
EXPORT_SYMBOL(thermal_zone_device_unregister);
#ifdef CONFIG_NET
@@ -1312,10 +1295,11 @@ int thermal_generate_netlink_event(u32 orig, enum events event)
void *msg_header;
int size;
int result;
+ static unsigned int thermal_event_seqnum;
/* allocate memory */
- size = nla_total_size(sizeof(struct thermal_genl_event)) + \
- nla_total_size(0);
+ size = nla_total_size(sizeof(struct thermal_genl_event)) +
+ nla_total_size(0);
skb = genlmsg_new(size, GFP_ATOMIC);
if (!skb)
@@ -1331,8 +1315,8 @@ int thermal_generate_netlink_event(u32 orig, enum events event)
}
/* fill the data */
- attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT, \
- sizeof(struct thermal_genl_event));
+ attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT,
+ sizeof(struct thermal_genl_event));
if (!attr) {
nlmsg_free(skb);
@@ -1359,7 +1343,7 @@ int thermal_generate_netlink_event(u32 orig, enum events event)
result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC);
if (result)
- printk(KERN_INFO "failed to send netlink event:%d", result);
+ pr_info("failed to send netlink event:%d\n", result);
return result;
}
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index afadcd43..24145c3 100644
--- a/drivers/tty/amiserial.c
+++ b/drivers/tty/amiserial.c
@@ -85,7 +85,6 @@ static char *serial_version = "4.30";
#include <asm/setup.h>
-#include <asm/system.h>
#include <asm/irq.h>
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c
index 3a0d53d..ee30779 100644
--- a/drivers/tty/hvc/hvc_vio.c
+++ b/drivers/tty/hvc/hvc_vio.c
@@ -310,11 +310,8 @@ static int __devexit hvc_vio_remove(struct vio_dev *vdev)
static struct vio_driver hvc_vio_driver = {
.id_table = hvc_driver_table,
.probe = hvc_vio_probe,
- .remove = __devexit_p(hvc_vio_remove),
- .driver = {
- .name = hvc_driver_name,
- .owner = THIS_MODULE,
- }
+ .remove = hvc_vio_remove,
+ .name = hvc_driver_name,
};
static int __init hvc_vio_init(void)
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index d237591..3436436 100644
--- a/drivers/tty/hvc/hvcs.c
+++ b/drivers/tty/hvc/hvcs.c
@@ -879,10 +879,7 @@ static struct vio_driver hvcs_vio_driver = {
.id_table = hvcs_driver_table,
.probe = hvcs_probe,
.remove = __devexit_p(hvcs_remove),
- .driver = {
- .name = hvcs_driver_name,
- .owner = THIS_MODULE,
- }
+ .name = hvcs_driver_name,
};
/* Only called from hvcs_get_pi please */
diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c
index 03c1497..e1235ac 100644
--- a/drivers/tty/isicom.c
+++ b/drivers/tty/isicom.c
@@ -102,7 +102,7 @@
* You can find the original tools for this direct from Multitech
* ftp://ftp.multitech.com/ISI-Cards/
*
- * Having installed the cards the module options (/etc/modprobe.conf)
+ * Having installed the cards the module options (/etc/modprobe.d/)
*
* options isicom io=card1,card2,card3,card4 irq=card1,card2,card3,card4
*
@@ -133,7 +133,6 @@
#include <linux/uaccess.h>
#include <linux/io.h>
-#include <asm/system.h>
#include <linux/pci.h>
diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c
index 8a8d044..324467d 100644
--- a/drivers/tty/moxa.c
+++ b/drivers/tty/moxa.c
@@ -46,7 +46,6 @@
#include <linux/slab.h>
#include <linux/ratelimit.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>
diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c
index 17ff377..c6f372d 100644
--- a/drivers/tty/mxser.c
+++ b/drivers/tty/mxser.c
@@ -41,7 +41,6 @@
#include <linux/slab.h>
#include <linux/ratelimit.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c
index a09ce3e..1b2db9a 100644
--- a/drivers/tty/n_hdlc.c
+++ b/drivers/tty/n_hdlc.c
@@ -102,7 +102,6 @@
#include <linux/if.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/termios.h>
#include <asm/uaccess.h>
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index d2256d0..94b6eda 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -50,7 +50,6 @@
#include <linux/uaccess.h>
#include <linux/module.h>
-#include <asm/system.h>
/* number of characters left in xmit buffer before select has we have room */
#define WAKEUP_CHARS 256
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index f96ecae..eeae7fa 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -27,7 +27,6 @@
#include <linux/devpts_fs.h>
#include <linux/slab.h>
-#include <asm/system.h>
#ifdef CONFIG_UNIX98_PTYS
static struct tty_driver *ptm_driver;
diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c
index f899996..a44345a 100644
--- a/drivers/tty/serial/21285.c
+++ b/drivers/tty/serial/21285.c
@@ -16,6 +16,7 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
+#include <asm/system_info.h>
#include <asm/hardware/dec21285.h>
#include <mach/hardware.h>
diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c
index 7398390..5ce7825 100644
--- a/drivers/tty/serial/68328serial.c
+++ b/drivers/tty/serial/68328serial.c
@@ -39,7 +39,6 @@
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c
index 8609060..29b695d 100644
--- a/drivers/tty/serial/8250/serial_cs.c
+++ b/drivers/tty/serial/8250/serial_cs.c
@@ -43,7 +43,6 @@
#include <linux/delay.h>
#include <linux/major.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 20d795d..0c65c9e 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -51,6 +51,7 @@
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/delay.h>
+#include <linux/types.h>
#include <asm/io.h>
#include <asm/sizes.h>
@@ -271,6 +272,7 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.direction = DMA_MEM_TO_DEV,
.dst_maxburst = uap->fifosize >> 1,
+ .device_fc = false,
};
struct dma_chan *chan;
dma_cap_mask_t mask;
@@ -304,6 +306,7 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.direction = DMA_DEV_TO_MEM,
.src_maxburst = uap->fifosize >> 1,
+ .device_fc = false,
};
chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param);
@@ -481,7 +484,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap)
return -EBUSY;
}
- desc = dma_dev->device_prep_slave_sg(chan, &dmatx->sg, 1, DMA_MEM_TO_DEV,
+ desc = dmaengine_prep_slave_sg(chan, &dmatx->sg, 1, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dma_unmap_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE);
@@ -664,7 +667,6 @@ static void pl011_dma_rx_callback(void *data);
static int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap)
{
struct dma_chan *rxchan = uap->dmarx.chan;
- struct dma_device *dma_dev;
struct pl011_dmarx_data *dmarx = &uap->dmarx;
struct dma_async_tx_descriptor *desc;
struct pl011_sgbuf *sgbuf;
@@ -675,8 +677,7 @@ static int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap)
/* Start the RX DMA job */
sgbuf = uap->dmarx.use_buf_b ?
&uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a;
- dma_dev = rxchan->device;
- desc = rxchan->device->device_prep_slave_sg(rxchan, &sgbuf->sg, 1,
+ desc = dmaengine_prep_slave_sg(rxchan, &sgbuf->sg, 1,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
/*
diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c
index 23d7916..5b07c0c 100644
--- a/drivers/tty/serial/crisv10.c
+++ b/drivers/tty/serial/crisv10.c
@@ -34,9 +34,9 @@ static char *serial_version = "$Revision: 1.25 $";
#include <asm/irq.h>
#include <asm/dma.h>
-#include <asm/system.h>
#include <arch/svinto.h>
+#include <arch/system.h>
/* non-arch dependent serial structures are in linux/serial.h */
#include <linux/serial.h>
diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c
index e3699a8..6491b86 100644
--- a/drivers/tty/serial/dz.c
+++ b/drivers/tty/serial/dz.c
@@ -52,7 +52,6 @@
#include <linux/atomic.h>
#include <asm/bootinfo.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/dec/interrupts.h>
#include <asm/dec/kn01.h>
diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c
index d55709a..defc4e3 100644
--- a/drivers/tty/serial/icom.c
+++ b/drivers/tty/serial/icom.c
@@ -52,7 +52,6 @@
#include <linux/firmware.h>
#include <linux/bitops.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 5e85e1e..fca13dc 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -50,7 +50,6 @@
#include <linux/atomic.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <mach/hardware.h>
#include <mach/dma.h>
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 332f2eb..e825460 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -844,7 +844,7 @@ static int dma_handle_rx(struct eg20t_port *priv)
sg_dma_address(sg) = priv->rx_buf_dma;
- desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx,
+ desc = dmaengine_prep_slave_sg(priv->chan_rx,
sg, 1, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
@@ -1003,7 +1003,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv)
sg_dma_len(sg) = size;
}
- desc = priv->chan_tx->device->device_prep_slave_sg(priv->chan_tx,
+ desc = dmaengine_prep_slave_sg(priv->chan_tx,
priv->sg_tx_p, nent, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c
index ef7a21a..2ca5959 100644
--- a/drivers/tty/serial/sa1100.c
+++ b/drivers/tty/serial/sa1100.c
@@ -38,6 +38,7 @@
#include <asm/irq.h>
#include <mach/hardware.h>
+#include <mach/irqs.h>
#include <asm/mach/serial_sa1100.h>
/* We've been assigned a range on the "Low-density serial ports" major */
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 61b7fd2..bf461cf 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1229,17 +1229,20 @@ static void sci_dma_tx_complete(void *arg)
port->icount.tx += sg_dma_len(&s->sg_tx);
async_tx_ack(s->desc_tx);
- s->cookie_tx = -EINVAL;
s->desc_tx = NULL;
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (!uart_circ_empty(xmit)) {
+ s->cookie_tx = 0;
schedule_work(&s->work_tx);
- } else if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
- u16 ctrl = sci_in(port, SCSCR);
- sci_out(port, SCSCR, ctrl & ~SCSCR_TIE);
+ } else {
+ s->cookie_tx = -EINVAL;
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+ u16 ctrl = sci_in(port, SCSCR);
+ sci_out(port, SCSCR, ctrl & ~SCSCR_TIE);
+ }
}
spin_unlock_irqrestore(&port->lock, flags);
@@ -1338,7 +1341,7 @@ static void sci_submit_rx(struct sci_port *s)
struct scatterlist *sg = &s->sg_rx[i];
struct dma_async_tx_descriptor *desc;
- desc = chan->device->device_prep_slave_sg(chan,
+ desc = dmaengine_prep_slave_sg(chan,
sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
if (desc) {
@@ -1453,7 +1456,7 @@ static void work_fn_tx(struct work_struct *work)
BUG_ON(!sg_dma_len(sg));
- desc = chan->device->device_prep_slave_sg(chan,
+ desc = dmaengine_prep_slave_sg(chan,
sg, s->sg_len_tx, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
@@ -1501,8 +1504,10 @@ static void sci_start_tx(struct uart_port *port)
}
if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) &&
- s->cookie_tx < 0)
+ s->cookie_tx < 0) {
+ s->cookie_tx = 0;
schedule_work(&s->work_tx);
+ }
#endif
if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index 3ba5d28..505961c 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -23,6 +23,7 @@
#include <asm/spitfire.h>
#include <asm/prom.h>
#include <asm/irq.h>
+#include <asm/setup.h>
#if defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c
index 62dacd0..f0d93eb 100644
--- a/drivers/tty/serial/sunsab.c
+++ b/drivers/tty/serial/sunsab.c
@@ -37,6 +37,7 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/prom.h>
+#include <asm/setup.h>
#if defined(CONFIG_SERIAL_SUNSAB_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c
index d3ca6da..675303b 100644
--- a/drivers/tty/serial/sunsu.c
+++ b/drivers/tty/serial/sunsu.c
@@ -41,6 +41,7 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/prom.h>
+#include <asm/setup.h>
#if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c
index da44158..b3b70b0 100644
--- a/drivers/tty/serial/sunzilog.c
+++ b/drivers/tty/serial/sunzilog.c
@@ -37,6 +37,7 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/prom.h>
+#include <asm/setup.h>
#if defined(CONFIG_SERIAL_SUNZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c
index b7455b5..4001eee 100644
--- a/drivers/tty/serial/zs.c
+++ b/drivers/tty/serial/zs.c
@@ -67,7 +67,6 @@
#include <linux/types.h>
#include <linux/atomic.h>
-#include <asm/system.h>
#include <asm/dec/interrupts.h>
#include <asm/dec/ioasic_addrs.h>
diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c
index 8e518da..593d40a 100644
--- a/drivers/tty/synclink.c
+++ b/drivers/tty/synclink.c
@@ -86,7 +86,6 @@
#include <linux/ioctl.h>
#include <linux/synclink.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/dma.h>
diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c
index 34b1a3c..aa1debf 100644
--- a/drivers/tty/synclink_gt.c
+++ b/drivers/tty/synclink_gt.c
@@ -73,7 +73,6 @@
#include <linux/hdlc.h>
#include <linux/synclink.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/dma.h>
diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c
index 4fb6c4b..a3dddc1 100644
--- a/drivers/tty/synclinkmp.c
+++ b/drivers/tty/synclinkmp.c
@@ -58,7 +58,6 @@
#include <linux/delay.h>
#include <linux/ioctl.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/dma.h>
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index dd8a938..d939bd7 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -97,7 +97,6 @@
#include <linux/ratelimit.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#include <linux/kbd_kern.h>
#include <linux/vt_kern.h>
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index 9314d93..a1b9a2f 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -23,7 +23,6 @@
#include <asm/io.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#undef TTY_DEBUG_WAIT_UNTIL_SENT
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 84c4a7d..3bdd4b1 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -99,7 +99,6 @@
#include <linux/notifier.h>
#include <linux/device.h>
#include <linux/io.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include <linux/kdb.h>
#include <linux/ctype.h>
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 48ac6e7..cbd8f5f 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -44,7 +44,7 @@ config USB_ARCH_HAS_EHCI
default y if PPC_MPC512x
default y if ARCH_IXP4XX
default y if ARCH_W90X900
- default y if ARCH_AT91SAM9G45
+ default y if ARCH_AT91
default y if ARCH_MXC
default y if ARCH_OMAP3
default y if ARCH_CNS3XXX
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 26c0b75..2633f75 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -137,7 +137,7 @@ choice
config USB_AT91
tristate "Atmel AT91 USB Device Port"
- depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91SAM9G45
+ depends on ARCH_AT91
help
Many Atmel AT91 processors (such as the AT91RM2000) have a
full speed USB Device Port with support for five configurable
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
index 2204a4c..7777927 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/amd5536udc.c
@@ -54,7 +54,6 @@
#include <linux/prefetch.h>
#include <asm/byteorder.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
/* gadget stack */
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index 2db5f68..0c935d7 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -29,12 +29,13 @@
#include <linux/clk.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <asm/byteorder.h>
#include <mach/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/gpio.h>
#include <mach/board.h>
@@ -1707,7 +1708,27 @@ static void at91udc_shutdown(struct platform_device *dev)
spin_unlock_irqrestore(&udc->lock, flags);
}
-static int __init at91udc_probe(struct platform_device *pdev)
+static void __devinit at91udc_of_init(struct at91_udc *udc,
+ struct device_node *np)
+{
+ struct at91_udc_data *board = &udc->board;
+ u32 val;
+ enum of_gpio_flags flags;
+
+ if (of_property_read_u32(np, "atmel,vbus-polled", &val) == 0)
+ board->vbus_polled = 1;
+
+ board->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0,
+ &flags);
+ board->vbus_active_low = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0;
+
+ board->pullup_pin = of_get_named_gpio_flags(np, "atmel,pullup-gpio", 0,
+ &flags);
+
+ board->pullup_active_low = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0;
+}
+
+static int __devinit at91udc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct at91_udc *udc;
@@ -1742,7 +1763,11 @@ static int __init at91udc_probe(struct platform_device *pdev)
/* init software state */
udc = &controller;
udc->gadget.dev.parent = dev;
- udc->board = *(struct at91_udc_data *) dev->platform_data;
+ if (pdev->dev.of_node)
+ at91udc_of_init(udc, pdev->dev.of_node);
+ else
+ memcpy(&udc->board, dev->platform_data,
+ sizeof(struct at91_udc_data));
udc->pdev = pdev;
udc->enabled = 0;
spin_lock_init(&udc->lock);
@@ -1971,6 +1996,15 @@ static int at91udc_resume(struct platform_device *pdev)
#define at91udc_resume NULL
#endif
+#if defined(CONFIG_OF)
+static const struct of_device_id at91_udc_dt_ids[] = {
+ { .compatible = "atmel,at91rm9200-udc" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, at91_udc_dt_ids);
+#endif
+
static struct platform_driver at91_udc_driver = {
.remove = __exit_p(at91udc_remove),
.shutdown = at91udc_shutdown,
@@ -1979,6 +2013,7 @@ static struct platform_driver at91_udc_driver = {
.driver = {
.name = (char *) driver_name,
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(at91_udc_dt_ids),
},
};
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index e1cd56c..a6dfd21 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -44,7 +44,6 @@
#include <asm/byteorder.h>
#include <linux/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#define DRIVER_DESC "USB Host+Gadget Emulator"
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index b30e21f..5f94e79 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -43,7 +43,6 @@
#include <asm/byteorder.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/dma.h>
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index e1dfd32..e151d6b 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -43,7 +43,6 @@
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c
index edd52d9..f9cedd5 100644
--- a/drivers/usb/gadget/langwell_udc.c
+++ b/drivers/usb/gadget/langwell_udc.c
@@ -32,7 +32,6 @@
#include <linux/pm.h>
#include <linux/io.h>
#include <linux/irq.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#include "langwell_udc.h"
diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c
index 19bbe80..a73cf40 100644
--- a/drivers/usb/gadget/mv_udc_core.c
+++ b/drivers/usb/gadget/mv_udc_core.c
@@ -34,7 +34,6 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/platform_data/mv_usb.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#include "mv_udc.h"
diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c
index 01ae56f..43ac748 100644
--- a/drivers/usb/gadget/net2272.c
+++ b/drivers/usb/gadget/net2272.c
@@ -42,7 +42,6 @@
#include <linux/usb/gadget.h>
#include <asm/byteorder.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#include "net2272.h"
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index a5ccabc..ac335af 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -59,7 +59,6 @@
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index b44830d..3b4b6dd 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -40,7 +40,6 @@
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/mach-types.h>
diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c
index d83134b..4e4dc1f 100644
--- a/drivers/usb/gadget/printer.c
+++ b/drivers/usb/gadget/printer.c
@@ -34,7 +34,6 @@
#include <asm/byteorder.h>
#include <linux/io.h>
#include <linux/irq.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index 1b33634..41ed69c 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -41,7 +41,6 @@
#include <asm/byteorder.h>
#include <asm/dma.h>
#include <asm/gpio.h>
-#include <asm/system.h>
#include <asm/mach-types.h>
#include <asm/unaligned.h>
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index d3cdffe..73a934a 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -34,7 +34,6 @@
#include <asm/io.h>
#include <asm/byteorder.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
index ab9c65e..195524c 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -37,7 +37,6 @@
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#include <mach/irqs.h>
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index a5a3ef1..19f318a 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -13,6 +13,7 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/of_platform.h>
/* interface and function clocks */
static struct clk *iclk, *fclk;
@@ -115,6 +116,8 @@ static const struct hc_driver ehci_atmel_hc_driver = {
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
+static u64 at91_ehci_dma_mask = DMA_BIT_MASK(32);
+
static int __devinit ehci_atmel_drv_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd;
@@ -137,6 +140,13 @@ static int __devinit ehci_atmel_drv_probe(struct platform_device *pdev)
goto fail_create_hcd;
}
+ /* Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &at91_ehci_dma_mask;
+
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
retval = -ENOMEM;
@@ -225,9 +235,21 @@ static int __devexit ehci_atmel_drv_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_ehci_dt_ids[] = {
+ { .compatible = "atmel,at91sam9g45-ehci" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_ehci_dt_ids);
+#endif
+
static struct platform_driver ehci_atmel_driver = {
.probe = ehci_atmel_drv_probe,
.remove = __devexit_p(ehci_atmel_drv_remove),
.shutdown = usb_hcd_platform_shutdown,
- .driver.name = "atmel-ehci",
+ .driver = {
+ .name = "atmel-ehci",
+ .of_match_table = of_match_ptr(atmel_ehci_dt_ids),
+ },
};
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index aede637..057cdda 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -45,7 +45,6 @@
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#if defined(CONFIG_PPC_PS3)
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 9248800..9e65e30 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -70,7 +70,6 @@
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
#include "isp116x.h"
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 9e63cdf..2ed112d 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -84,7 +84,6 @@
#include <linux/prefetch.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 8e855eb..db8963f 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -14,6 +14,8 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
#include <mach/hardware.h>
#include <asm/gpio.h>
@@ -477,13 +479,109 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
return IRQ_HANDLED;
}
+#ifdef CONFIG_OF
+static const struct of_device_id at91_ohci_dt_ids[] = {
+ { .compatible = "atmel,at91rm9200-ohci" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, at91_ohci_dt_ids);
+
+static u64 at91_ohci_dma_mask = DMA_BIT_MASK(32);
+
+static int __devinit ohci_at91_of_init(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int i, ret, gpio;
+ enum of_gpio_flags flags;
+ struct at91_usbh_data *pdata;
+ u32 ports;
+
+ if (!np)
+ return 0;
+
+ /* Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &at91_ohci_dma_mask;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (!of_property_read_u32(np, "num-ports", &ports))
+ pdata->ports = ports;
+
+ for (i = 0; i < 2; i++) {
+ gpio = of_get_named_gpio_flags(np, "atmel,vbus-gpio", i, &flags);
+ pdata->vbus_pin[i] = gpio;
+ if (!gpio_is_valid(gpio))
+ continue;
+ pdata->vbus_pin_active_low[i] = flags & OF_GPIO_ACTIVE_LOW;
+ ret = gpio_request(gpio, "ohci_vbus");
+ if (ret) {
+ dev_warn(&pdev->dev, "can't request vbus gpio %d", gpio);
+ continue;
+ }
+ ret = gpio_direction_output(gpio, !(flags & OF_GPIO_ACTIVE_LOW) ^ 1);
+ if (ret)
+ dev_warn(&pdev->dev, "can't put vbus gpio %d as output %d",
+ !(flags & OF_GPIO_ACTIVE_LOW) ^ 1, gpio);
+ }
+
+ for (i = 0; i < 2; i++) {
+ gpio = of_get_named_gpio_flags(np, "atmel,oc-gpio", i, &flags);
+ pdata->overcurrent_pin[i] = gpio;
+ if (!gpio_is_valid(gpio))
+ continue;
+ ret = gpio_request(gpio, "ohci_overcurrent");
+ if (ret) {
+ dev_err(&pdev->dev, "can't request overcurrent gpio %d", gpio);
+ continue;
+ }
+
+ ret = gpio_direction_input(gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "can't configure overcurrent gpio %d as input", gpio);
+ continue;
+ }
+
+ ret = request_irq(gpio_to_irq(gpio),
+ ohci_hcd_at91_overcurrent_irq,
+ IRQF_SHARED, "ohci_overcurrent", pdev);
+ if (ret) {
+ gpio_free(gpio);
+ dev_warn(& pdev->dev, "cannot get GPIO IRQ for overcurrent\n");
+ }
+ }
+
+ pdev->dev.platform_data = pdata;
+
+ return 0;
+}
+#else
+static int __devinit ohci_at91_of_init(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+
/*-------------------------------------------------------------------------*/
static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
{
- struct at91_usbh_data *pdata = pdev->dev.platform_data;
+ struct at91_usbh_data *pdata;
int i;
+ i = ohci_at91_of_init(pdev);
+
+ if (i)
+ return i;
+
+ pdata = pdev->dev.platform_data;
+
if (pdata) {
for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) {
if (!gpio_is_valid(pdata->vbus_pin[i]))
@@ -596,5 +694,6 @@ static struct platform_driver ohci_hcd_at91_driver = {
.driver = {
.name = "at91_ohci",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(at91_ohci_dt_ids),
},
};
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 543e90e..235171f 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -42,7 +42,6 @@
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index 4bde4f9..e1004fb 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -16,29 +16,115 @@
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <mach/assabet.h>
-#include <mach/badge4.h>
#include <asm/hardware/sa1111.h>
#ifndef CONFIG_SA1111
#error "This file is SA-1111 bus glue. CONFIG_SA1111 must be defined."
#endif
-extern int usb_disabled(void);
+#define USB_STATUS 0x0118
+#define USB_RESET 0x011c
+#define USB_IRQTEST 0x0120
+
+#define USB_RESET_FORCEIFRESET (1 << 0)
+#define USB_RESET_FORCEHCRESET (1 << 1)
+#define USB_RESET_CLKGENRESET (1 << 2)
+#define USB_RESET_SIMSCALEDOWN (1 << 3)
+#define USB_RESET_USBINTTEST (1 << 4)
+#define USB_RESET_SLEEPSTBYEN (1 << 5)
+#define USB_RESET_PWRSENSELOW (1 << 6)
+#define USB_RESET_PWRCTRLLOW (1 << 7)
+
+#define USB_STATUS_IRQHCIRMTWKUP (1 << 7)
+#define USB_STATUS_IRQHCIBUFFACC (1 << 8)
+#define USB_STATUS_NIRQHCIM (1 << 9)
+#define USB_STATUS_NHCIMFCLR (1 << 10)
+#define USB_STATUS_USBPWRSENSE (1 << 11)
-/*-------------------------------------------------------------------------*/
+#if 0
+static void dump_hci_status(struct usb_hcd *hcd, const char *label)
+{
+ unsigned long status = sa1111_readl(hcd->regs + USB_STATUS);
+
+ dbg("%s USB_STATUS = { %s%s%s%s%s}", label,
+ ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""),
+ ((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""),
+ ((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "),
+ ((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "),
+ ((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : ""));
+}
+#endif
-static void sa1111_start_hc(struct sa1111_dev *dev)
+static int ohci_sa1111_reset(struct usb_hcd *hcd)
{
- unsigned int usb_rst = 0;
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ ohci_hcd_init(ohci);
+ return ohci_init(ohci);
+}
- printk(KERN_DEBUG "%s: starting SA-1111 OHCI USB Controller\n",
- __FILE__);
+static int __devinit ohci_sa1111_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
-#ifdef CONFIG_SA1100_BADGE4
- if (machine_is_badge4()) {
- badge4_set_5V(BADGE4_5V_USB, 1);
+ ret = ohci_run(ohci);
+ if (ret < 0) {
+ ohci_err(ohci, "can't start\n");
+ ohci_stop(hcd);
}
+ return ret;
+}
+
+static const struct hc_driver ohci_sa1111_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "SA-1111 OHCI",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ohci_sa1111_reset,
+ .start = ohci_sa1111_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+static int sa1111_start_hc(struct sa1111_dev *dev)
+{
+ unsigned int usb_rst = 0;
+ int ret;
+
+ dev_dbg(&dev->dev, "starting SA-1111 OHCI USB Controller\n");
if (machine_is_xp860() ||
machine_has_neponset() ||
@@ -51,220 +137,121 @@ static void sa1111_start_hc(struct sa1111_dev *dev)
* host controller in reset.
*/
sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
- dev->mapbase + SA1111_USB_RESET);
+ dev->mapbase + USB_RESET);
/*
* Now, carefully enable the USB clock, and take
* the USB host controller out of reset.
*/
- sa1111_enable_device(dev);
- udelay(11);
- sa1111_writel(usb_rst, dev->mapbase + SA1111_USB_RESET);
+ ret = sa1111_enable_device(dev);
+ if (ret == 0) {
+ udelay(11);
+ sa1111_writel(usb_rst, dev->mapbase + USB_RESET);
+ }
+
+ return ret;
}
static void sa1111_stop_hc(struct sa1111_dev *dev)
{
unsigned int usb_rst;
- printk(KERN_DEBUG "%s: stopping SA-1111 OHCI USB Controller\n",
- __FILE__);
+
+ dev_dbg(&dev->dev, "stopping SA-1111 OHCI USB Controller\n");
/*
* Put the USB host controller into reset.
*/
- usb_rst = sa1111_readl(dev->mapbase + SA1111_USB_RESET);
+ usb_rst = sa1111_readl(dev->mapbase + USB_RESET);
sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
- dev->mapbase + SA1111_USB_RESET);
+ dev->mapbase + USB_RESET);
/*
* Stop the USB clock.
*/
sa1111_disable_device(dev);
-
-#ifdef CONFIG_SA1100_BADGE4
- if (machine_is_badge4()) {
- /* Disable power to the USB bus */
- badge4_set_5V(BADGE4_5V_USB, 0);
- }
-#endif
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-#if 0
-static void dump_hci_status(struct usb_hcd *hcd, const char *label)
-{
- unsigned long status = sa1111_readl(hcd->regs + SA1111_USB_STATUS);
-
- dbg ("%s USB_STATUS = { %s%s%s%s%s}", label,
- ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""),
- ((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""),
- ((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "),
- ((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "),
- ((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : ""));
}
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-/* configure so an HC device and id are always provided */
-/* always called with process context; sleeping is OK */
-
/**
- * usb_hcd_sa1111_probe - initialize SA-1111-based HCDs
- * Context: !in_interrupt()
+ * ohci_hcd_sa1111_probe - initialize SA-1111-based HCDs
*
* Allocates basic resources for this USB host controller, and
- * then invokes the start() method for the HCD associated with it
- * through the hotplug entry's driver_data.
- *
- * Store this function in the HCD's struct pci_driver as probe().
+ * then invokes the start() method for the HCD associated with it.
*/
-int usb_hcd_sa1111_probe (const struct hc_driver *driver,
- struct sa1111_dev *dev)
+static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev)
{
struct usb_hcd *hcd;
- int retval;
+ int ret;
- hcd = usb_create_hcd (driver, &dev->dev, "sa1111");
+ if (usb_disabled())
+ return -ENODEV;
+
+ hcd = usb_create_hcd(&ohci_sa1111_hc_driver, &dev->dev, "sa1111");
if (!hcd)
return -ENOMEM;
+
hcd->rsrc_start = dev->res.start;
hcd->rsrc_len = resource_size(&dev->res);
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
dbg("request_mem_region failed");
- retval = -EBUSY;
+ ret = -EBUSY;
goto err1;
}
+
hcd->regs = dev->mapbase;
- sa1111_start_hc(dev);
- ohci_hcd_init(hcd_to_ohci(hcd));
+ ret = sa1111_start_hc(dev);
+ if (ret)
+ goto err2;
- retval = usb_add_hcd(hcd, dev->irq[1], 0);
- if (retval == 0)
- return retval;
+ ret = usb_add_hcd(hcd, dev->irq[1], 0);
+ if (ret == 0)
+ return ret;
sa1111_stop_hc(dev);
+ err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
- return retval;
+ return ret;
}
-
-/* may be called without controller electrically present */
-/* may be called with controller, bus, and devices active */
-
/**
- * usb_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs
+ * ohci_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs
* @dev: USB Host Controller being removed
- * Context: !in_interrupt()
- *
- * Reverses the effect of usb_hcd_sa1111_probe(), first invoking
- * the HCD's stop() method. It is always called from a thread
- * context, normally "rmmod", "apmd", or something similar.
*
+ * Reverses the effect of ohci_hcd_sa1111_probe(), first invoking
+ * the HCD's stop() method.
*/
-void usb_hcd_sa1111_remove (struct usb_hcd *hcd, struct sa1111_dev *dev)
+static int ohci_hcd_sa1111_remove(struct sa1111_dev *dev)
{
+ struct usb_hcd *hcd = sa1111_get_drvdata(dev);
+
usb_remove_hcd(hcd);
sa1111_stop_hc(dev);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
-}
-
-/*-------------------------------------------------------------------------*/
-static int __devinit
-ohci_sa1111_start (struct usb_hcd *hcd)
-{
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);
- int ret;
-
- if ((ret = ohci_init(ohci)) < 0)
- return ret;
-
- if ((ret = ohci_run (ohci)) < 0) {
- err ("can't start %s", hcd->self.bus_name);
- ohci_stop (hcd);
- return ret;
- }
return 0;
}
-/*-------------------------------------------------------------------------*/
-
-static const struct hc_driver ohci_sa1111_hc_driver = {
- .description = hcd_name,
- .product_desc = "SA-1111 OHCI",
- .hcd_priv_size = sizeof(struct ohci_hcd),
-
- /*
- * generic hardware linkage
- */
- .irq = ohci_irq,
- .flags = HCD_USB11 | HCD_MEMORY,
-
- /*
- * basic lifecycle operations
- */
- .start = ohci_sa1111_start,
- .stop = ohci_stop,
-
- /*
- * managing i/o requests and associated device resources
- */
- .urb_enqueue = ohci_urb_enqueue,
- .urb_dequeue = ohci_urb_dequeue,
- .endpoint_disable = ohci_endpoint_disable,
-
- /*
- * scheduling support
- */
- .get_frame_number = ohci_get_frame,
-
- /*
- * root hub support
- */
- .hub_status_data = ohci_hub_status_data,
- .hub_control = ohci_hub_control,
-#ifdef CONFIG_PM
- .bus_suspend = ohci_bus_suspend,
- .bus_resume = ohci_bus_resume,
-#endif
- .start_port_reset = ohci_start_port_reset,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static int ohci_hcd_sa1111_drv_probe(struct sa1111_dev *dev)
-{
- int ret;
-
- if (usb_disabled())
- return -ENODEV;
-
- ret = usb_hcd_sa1111_probe(&ohci_sa1111_hc_driver, dev);
- return ret;
-}
-
-static int ohci_hcd_sa1111_drv_remove(struct sa1111_dev *dev)
+static void ohci_hcd_sa1111_shutdown(struct sa1111_dev *dev)
{
struct usb_hcd *hcd = sa1111_get_drvdata(dev);
- usb_hcd_sa1111_remove(hcd, dev);
- return 0;
+ if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ hcd->driver->shutdown(hcd);
+ sa1111_stop_hc(dev);
+ }
}
static struct sa1111_driver ohci_hcd_sa1111_driver = {
.drv = {
.name = "sa1111-ohci",
+ .owner = THIS_MODULE,
},
.devid = SA1111_DEVID_USB,
- .probe = ohci_hcd_sa1111_drv_probe,
- .remove = ohci_hcd_sa1111_drv_remove,
+ .probe = ohci_hcd_sa1111_probe,
+ .remove = ohci_hcd_sa1111_remove,
+ .shutdown = ohci_hcd_sa1111_shutdown,
};
-
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index 015c7c6..3b38030 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -40,7 +40,6 @@
#include <linux/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#include <linux/irq.h>
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 2a2cce2..91ce1c0 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -51,7 +51,6 @@
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index 16dd6a6..dbbd1ba 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -55,7 +55,6 @@
#include <linux/mutex.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include <asm/byteorder.h>
/* FIXME ohci.h is ONLY for internal use by the OHCI driver.
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index e37dea8..e4db350 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -45,7 +45,6 @@
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include "uhci-hcd.h"
diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c
index 97cb459..d05c7fb 100644
--- a/drivers/usb/musb/ux500_dma.c
+++ b/drivers/usb/musb/ux500_dma.c
@@ -115,12 +115,12 @@ static bool ux500_configure_channel(struct dma_channel *channel,
slave_conf.dst_addr = usb_fifo_addr;
slave_conf.dst_addr_width = addr_width;
slave_conf.dst_maxburst = 16;
+ slave_conf.device_fc = false;
dma_chan->device->device_control(dma_chan, DMA_SLAVE_CONFIG,
(unsigned long) &slave_conf);
- dma_desc = dma_chan->device->
- device_prep_slave_sg(dma_chan, &sg, 1, direction,
+ dma_desc = dmaengine_prep_slave_sg(dma_chan, &sg, 1, direction,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!dma_desc)
return false;
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 3648c82..6ec7f83 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -786,9 +786,8 @@ static void xfer_work(struct work_struct *work)
sg_dma_address(&sg) = pkt->dma + pkt->actual;
sg_dma_len(&sg) = pkt->trans;
- desc = chan->device->device_prep_slave_sg(chan, &sg, 1, dir,
- DMA_PREP_INTERRUPT |
- DMA_CTRL_ACK);
+ desc = dmaengine_prep_slave_sg(chan, &sg, 1, dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc)
return;
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 7c229d3..ff8605b 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1724,7 +1724,8 @@ static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv)
/*
* Module parameter to control latency timer for NDI FTDI-based USB devices.
- * If this value is not set in modprobe.conf.local its value will be set to 1ms.
+ * If this value is not set in /etc/modprobe.d/ its value will be set
+ * to 1ms.
*/
static int ndi_latency_timer = 1;
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index fe2d803..7691c86 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -222,7 +222,7 @@ config USB_LIBUSUAL
for usb-storage and ub drivers, and allows to switch binding
of these devices without rebuilding modules.
- Typical syntax of /etc/modprobe.conf is:
+ Typical syntax of /etc/modprobe.d/*conf is:
options libusual bias="ub"
diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c
index f23cae0..887df9d 100644
--- a/drivers/video/amifb.c
+++ b/drivers/video/amifb.c
@@ -53,7 +53,6 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c
index 915943a..f49181c 100644
--- a/drivers/video/backlight/88pm860x_bl.c
+++ b/drivers/video/backlight/88pm860x_bl.c
@@ -67,6 +67,28 @@ static inline int wled_idc(int port)
return ret;
}
+static int backlight_power_set(struct pm860x_chip *chip, int port,
+ int on)
+{
+ int ret = -EINVAL;
+
+ switch (port) {
+ case PM8606_BACKLIGHT1:
+ ret = on ? pm8606_osc_enable(chip, WLED1_DUTY) :
+ pm8606_osc_disable(chip, WLED1_DUTY);
+ break;
+ case PM8606_BACKLIGHT2:
+ ret = on ? pm8606_osc_enable(chip, WLED2_DUTY) :
+ pm8606_osc_disable(chip, WLED2_DUTY);
+ break;
+ case PM8606_BACKLIGHT3:
+ ret = on ? pm8606_osc_enable(chip, WLED3_DUTY) :
+ pm8606_osc_disable(chip, WLED3_DUTY);
+ break;
+ }
+ return ret;
+}
+
static int pm860x_backlight_set(struct backlight_device *bl, int brightness)
{
struct pm860x_backlight_data *data = bl_get_data(bl);
@@ -79,6 +101,9 @@ static int pm860x_backlight_set(struct backlight_device *bl, int brightness)
else
value = brightness;
+ if (brightness)
+ backlight_power_set(chip, data->port, 1);
+
ret = pm860x_reg_write(data->i2c, wled_a(data->port), value);
if (ret < 0)
goto out;
@@ -115,6 +140,9 @@ static int pm860x_backlight_set(struct backlight_device *bl, int brightness)
if (ret < 0)
goto out;
+ if (brightness == 0)
+ backlight_power_set(chip, data->port, 0);
+
dev_dbg(chip->dev, "set brightness %d\n", value);
data->current_brightness = value;
return 0;
@@ -170,7 +198,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
struct backlight_device *bl;
struct resource *res;
struct backlight_properties props;
- unsigned char value;
char name[MFD_NAME_SIZE];
int ret;
@@ -217,26 +244,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bl);
- /* Enable reference VSYS */
- ret = pm860x_reg_read(data->i2c, PM8606_VSYS);
- if (ret < 0)
- goto out;
- if ((ret & PM8606_VSYS_EN) == 0) {
- value = ret | PM8606_VSYS_EN;
- ret = pm860x_reg_write(data->i2c, PM8606_VSYS, value);
- if (ret < 0)
- goto out;
- }
- /* Enable reference OSC */
- ret = pm860x_reg_read(data->i2c, PM8606_MISC);
- if (ret < 0)
- goto out;
- if ((ret & PM8606_MISC_OSC_EN) == 0) {
- value = ret | PM8606_MISC_OSC_EN;
- ret = pm860x_reg_write(data->i2c, PM8606_MISC, value);
- if (ret < 0)
- goto out;
- }
/* read current backlight */
ret = pm860x_backlight_get_brightness(bl);
if (ret < 0)
diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c
index be98d15..a523b25 100644
--- a/drivers/video/backlight/apple_bl.c
+++ b/drivers/video/backlight/apple_bl.c
@@ -24,6 +24,7 @@
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/acpi.h>
+#include <linux/atomic.h>
static struct backlight_device *apple_backlight_device;
@@ -221,14 +222,32 @@ static struct acpi_driver apple_bl_driver = {
},
};
+static atomic_t apple_bl_registered = ATOMIC_INIT(0);
+
+int apple_bl_register(void)
+{
+ if (atomic_xchg(&apple_bl_registered, 1) == 0)
+ return acpi_bus_register_driver(&apple_bl_driver);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(apple_bl_register);
+
+void apple_bl_unregister(void)
+{
+ if (atomic_xchg(&apple_bl_registered, 0) == 1)
+ acpi_bus_unregister_driver(&apple_bl_driver);
+}
+EXPORT_SYMBOL_GPL(apple_bl_unregister);
+
static int __init apple_bl_init(void)
{
- return acpi_bus_register_driver(&apple_bl_driver);
+ return apple_bl_register();
}
static void __exit apple_bl_exit(void)
{
- acpi_bus_unregister_driver(&apple_bl_driver);
+ apple_bl_unregister();
}
module_init(apple_bl_init);
diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c
index a2161f6..2231aec 100644
--- a/drivers/video/backlight/tosa_lcd.c
+++ b/drivers/video/backlight/tosa_lcd.c
@@ -271,7 +271,7 @@ static int tosa_lcd_resume(struct spi_device *spi)
}
#else
#define tosa_lcd_suspend NULL
-#define tosa_lcd_reume NULL
+#define tosa_lcd_resume NULL
#endif
static struct spi_driver tosa_lcd_driver = {
diff --git a/drivers/video/bt431.h b/drivers/video/bt431.h
index c826f27..04e0cfb 100644
--- a/drivers/video/bt431.h
+++ b/drivers/video/bt431.h
@@ -8,7 +8,6 @@
* archive for more details.
*/
#include <linux/types.h>
-#include <asm/system.h>
/*
* Bt431 cursor generator registers, 32-bit aligned.
diff --git a/drivers/video/bt455.h b/drivers/video/bt455.h
index b7591fe..80f61b0 100644
--- a/drivers/video/bt455.h
+++ b/drivers/video/bt455.h
@@ -8,7 +8,6 @@
* archive for more details.
*/
#include <linux/types.h>
-#include <asm/system.h>
/*
* Bt455 byte-wide registers, 32-bit aligned.
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 8745637..2e471c2 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -77,7 +77,6 @@
#include <linux/crc32.h> /* For counting font checksums */
#include <asm/fb.h>
#include <asm/irq.h>
-#include <asm/system.h>
#include "fbcon.h"
diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c
index a122d92..6d15966 100644
--- a/drivers/video/console/newport_con.c
+++ b/drivers/video/console/newport_con.c
@@ -22,7 +22,6 @@
#include <asm/io.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/gio_device.h>
diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c
index 8503807..c1527f5 100644
--- a/drivers/video/cyber2000fb.c
+++ b/drivers/video/cyber2000fb.c
@@ -51,7 +51,6 @@
#include <linux/i2c-algo-bit.h>
#include <asm/pgtable.h>
-#include <asm/system.h>
#ifdef __arm__
#include <asm/mach-types.h>
diff --git a/drivers/video/dnfb.c b/drivers/video/dnfb.c
index ec56d25..49e3dda 100644
--- a/drivers/video/dnfb.c
+++ b/drivers/video/dnfb.c
@@ -7,7 +7,6 @@
#include <linux/platform_device.h>
#include <asm/setup.h>
-#include <asm/system.h>
#include <asm/irq.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c
index 727a514..eec0d7b 100644
--- a/drivers/video/mx3fb.c
+++ b/drivers/video/mx3fb.c
@@ -337,7 +337,7 @@ static void sdc_enable_channel(struct mx3fb_info *mx3_fbi)
/* This enables the channel */
if (mx3_fbi->cookie < 0) {
- mx3_fbi->txd = dma_chan->device->device_prep_slave_sg(dma_chan,
+ mx3_fbi->txd = dmaengine_prep_slave_sg(dma_chan,
&mx3_fbi->sg[0], 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
if (!mx3_fbi->txd) {
dev_err(mx3fb->dev, "Cannot allocate descriptor on %d\n",
@@ -1091,7 +1091,7 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var,
if (mx3_fbi->txd)
async_tx_ack(mx3_fbi->txd);
- txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg +
+ txd = dmaengine_prep_slave_sg(dma_chan, sg +
mx3_fbi->cur_ipu_buf, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
if (!txd) {
dev_err(fbi->device,
diff --git a/drivers/video/neofb.c b/drivers/video/neofb.c
index fb3f673..afc9521 100644
--- a/drivers/video/neofb.c
+++ b/drivers/video/neofb.c
@@ -71,7 +71,6 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pgtable.h>
-#include <asm/system.h>
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c
index fd22716..4e5b960 100644
--- a/drivers/video/omap2/vrfb.c
+++ b/drivers/video/omap2/vrfb.c
@@ -27,7 +27,6 @@
#include <linux/bitops.h>
#include <linux/mutex.h>
-#include <mach/io.h>
#include <plat/vrfb.h>
#include <plat/sdrc.h>
diff --git a/drivers/video/pmag-ba-fb.c b/drivers/video/pmag-ba-fb.c
index 0c69fa2..9b4a60b 100644
--- a/drivers/video/pmag-ba-fb.c
+++ b/drivers/video/pmag-ba-fb.c
@@ -33,7 +33,6 @@
#include <linux/types.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <video/pmag-ba-fb.h>
diff --git a/drivers/video/pmagb-b-fb.c b/drivers/video/pmagb-b-fb.c
index 22fcb9a..4e7a9c4 100644
--- a/drivers/video/pmagb-b-fb.c
+++ b/drivers/video/pmagb-b-fb.c
@@ -29,7 +29,6 @@
#include <linux/types.h>
#include <asm/io.h>
-#include <asm/system.h>
#include <video/pmagb-b-fb.h>
diff --git a/drivers/video/q40fb.c b/drivers/video/q40fb.c
index f5a39f5..a104e8c 100644
--- a/drivers/video/q40fb.c
+++ b/drivers/video/q40fb.c
@@ -20,7 +20,6 @@
#include <asm/uaccess.h>
#include <asm/setup.h>
-#include <asm/system.h>
#include <asm/q40_master.h>
#include <linux/fb.h>
#include <linux/module.h>
diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c
index 98d55d0..b632584 100644
--- a/drivers/video/sa1100fb.c
+++ b/drivers/video/sa1100fb.c
@@ -173,282 +173,48 @@
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
+#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/mutex.h>
#include <linux/io.h>
+#include <video/sa1100fb.h>
+
#include <mach/hardware.h>
#include <asm/mach-types.h>
-#include <mach/assabet.h>
#include <mach/shannon.h>
/*
- * debugging?
- */
-#define DEBUG 0
-/*
* Complain if VAR is out of range.
*/
#define DEBUG_VAR 1
-#undef ASSABET_PAL_VIDEO
-
#include "sa1100fb.h"
-extern void (*sa1100fb_backlight_power)(int on);
-extern void (*sa1100fb_lcd_power)(int on);
-
-static struct sa1100fb_rgb rgb_4 = {
+static const struct sa1100fb_rgb rgb_4 = {
.red = { .offset = 0, .length = 4, },
.green = { .offset = 0, .length = 4, },
.blue = { .offset = 0, .length = 4, },
.transp = { .offset = 0, .length = 0, },
};
-static struct sa1100fb_rgb rgb_8 = {
+static const struct sa1100fb_rgb rgb_8 = {
.red = { .offset = 0, .length = 8, },
.green = { .offset = 0, .length = 8, },
.blue = { .offset = 0, .length = 8, },
.transp = { .offset = 0, .length = 0, },
};
-static struct sa1100fb_rgb def_rgb_16 = {
+static const struct sa1100fb_rgb def_rgb_16 = {
.red = { .offset = 11, .length = 5, },
.green = { .offset = 5, .length = 6, },
.blue = { .offset = 0, .length = 5, },
.transp = { .offset = 0, .length = 0, },
};
-#ifdef CONFIG_SA1100_ASSABET
-#ifndef ASSABET_PAL_VIDEO
-/*
- * The assabet uses a sharp LQ039Q2DS54 LCD module. It is actually
- * takes an RGB666 signal, but we provide it with an RGB565 signal
- * instead (def_rgb_16).
- */
-static struct sa1100fb_mach_info lq039q2ds54_info __initdata = {
- .pixclock = 171521, .bpp = 16,
- .xres = 320, .yres = 240,
-
- .hsync_len = 5, .vsync_len = 1,
- .left_margin = 61, .upper_margin = 3,
- .right_margin = 9, .lower_margin = 0,
-
- .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-
- .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
- .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
-};
-#else
-static struct sa1100fb_mach_info pal_info __initdata = {
- .pixclock = 67797, .bpp = 16,
- .xres = 640, .yres = 512,
-
- .hsync_len = 64, .vsync_len = 6,
- .left_margin = 125, .upper_margin = 70,
- .right_margin = 115, .lower_margin = 36,
-
- .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
- .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512),
-};
-#endif
-#endif
-
-#ifdef CONFIG_SA1100_H3600
-static struct sa1100fb_mach_info h3600_info __initdata = {
- .pixclock = 174757, .bpp = 16,
- .xres = 320, .yres = 240,
-
- .hsync_len = 3, .vsync_len = 3,
- .left_margin = 12, .upper_margin = 10,
- .right_margin = 17, .lower_margin = 1,
-
- .cmap_static = 1,
-
- .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
- .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
-};
-
-static struct sa1100fb_rgb h3600_rgb_16 = {
- .red = { .offset = 12, .length = 4, },
- .green = { .offset = 7, .length = 4, },
- .blue = { .offset = 1, .length = 4, },
- .transp = { .offset = 0, .length = 0, },
-};
-#endif
-
-#ifdef CONFIG_SA1100_H3100
-static struct sa1100fb_mach_info h3100_info __initdata = {
- .pixclock = 406977, .bpp = 4,
- .xres = 320, .yres = 240,
-
- .hsync_len = 26, .vsync_len = 41,
- .left_margin = 4, .upper_margin = 0,
- .right_margin = 4, .lower_margin = 0,
-
- .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- .cmap_greyscale = 1,
- .cmap_inverse = 1,
-
- .lccr0 = LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas,
- .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
-};
-#endif
-
-#ifdef CONFIG_SA1100_COLLIE
-static struct sa1100fb_mach_info collie_info __initdata = {
- .pixclock = 171521, .bpp = 16,
- .xres = 320, .yres = 240,
-
- .hsync_len = 5, .vsync_len = 1,
- .left_margin = 11, .upper_margin = 2,
- .right_margin = 30, .lower_margin = 0,
-
- .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-
- .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
- .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
-};
-#endif
-
-#ifdef LART_GREY_LCD
-static struct sa1100fb_mach_info lart_grey_info __initdata = {
- .pixclock = 150000, .bpp = 4,
- .xres = 320, .yres = 240,
-
- .hsync_len = 1, .vsync_len = 1,
- .left_margin = 4, .upper_margin = 0,
- .right_margin = 2, .lower_margin = 0,
-
- .cmap_greyscale = 1,
- .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-
- .lccr0 = LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_4PixMono,
- .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512),
-};
-#endif
-#ifdef LART_COLOR_LCD
-static struct sa1100fb_mach_info lart_color_info __initdata = {
- .pixclock = 150000, .bpp = 16,
- .xres = 320, .yres = 240,
-
- .hsync_len = 2, .vsync_len = 3,
- .left_margin = 69, .upper_margin = 14,
- .right_margin = 8, .lower_margin = 4,
-
- .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
- .lccr3 = LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512),
-};
-#endif
-#ifdef LART_VIDEO_OUT
-static struct sa1100fb_mach_info lart_video_info __initdata = {
- .pixclock = 39721, .bpp = 16,
- .xres = 640, .yres = 480,
-
- .hsync_len = 95, .vsync_len = 2,
- .left_margin = 40, .upper_margin = 32,
- .right_margin = 24, .lower_margin = 11,
-
- .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-
- .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
- .lccr3 = LCCR3_OutEnL | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512),
-};
-#endif
-
-#ifdef LART_KIT01_LCD
-static struct sa1100fb_mach_info lart_kit01_info __initdata = {
- .pixclock = 63291, .bpp = 16,
- .xres = 640, .yres = 480,
-
- .hsync_len = 64, .vsync_len = 3,
- .left_margin = 122, .upper_margin = 45,
- .right_margin = 10, .lower_margin = 10,
-
- .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
- .lccr3 = LCCR3_OutEnH | LCCR3_PixFlEdg
-};
-#endif
-
-#ifdef CONFIG_SA1100_SHANNON
-static struct sa1100fb_mach_info shannon_info __initdata = {
- .pixclock = 152500, .bpp = 8,
- .xres = 640, .yres = 480,
-
- .hsync_len = 4, .vsync_len = 3,
- .left_margin = 2, .upper_margin = 0,
- .right_margin = 1, .lower_margin = 0,
-
- .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-
- .lccr0 = LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
- .lccr3 = LCCR3_ACBsDiv(512),
-};
-#endif
-
-static struct sa1100fb_mach_info * __init
-sa1100fb_get_machine_info(struct sa1100fb_info *fbi)
-{
- struct sa1100fb_mach_info *inf = NULL;
-
- /*
- * R G B T
- * default {11,5}, { 5,6}, { 0,5}, { 0,0}
- * h3600 {12,4}, { 7,4}, { 1,4}, { 0,0}
- * freebird { 8,4}, { 4,4}, { 0,4}, {12,4}
- */
-#ifdef CONFIG_SA1100_ASSABET
- if (machine_is_assabet()) {
-#ifndef ASSABET_PAL_VIDEO
- inf = &lq039q2ds54_info;
-#else
- inf = &pal_info;
-#endif
- }
-#endif
-#ifdef CONFIG_SA1100_H3100
- if (machine_is_h3100()) {
- inf = &h3100_info;
- }
-#endif
-#ifdef CONFIG_SA1100_H3600
- if (machine_is_h3600()) {
- inf = &h3600_info;
- fbi->rgb[RGB_16] = &h3600_rgb_16;
- }
-#endif
-#ifdef CONFIG_SA1100_COLLIE
- if (machine_is_collie()) {
- inf = &collie_info;
- }
-#endif
-#ifdef CONFIG_SA1100_LART
- if (machine_is_lart()) {
-#ifdef LART_GREY_LCD
- inf = &lart_grey_info;
-#endif
-#ifdef LART_COLOR_LCD
- inf = &lart_color_info;
-#endif
-#ifdef LART_VIDEO_OUT
- inf = &lart_video_info;
-#endif
-#ifdef LART_KIT01_LCD
- inf = &lart_kit01_info;
-#endif
- }
-#endif
-#ifdef CONFIG_SA1100_SHANNON
- if (machine_is_shannon()) {
- inf = &shannon_info;
- }
-#endif
- return inf;
-}
-
static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *);
static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state);
@@ -533,7 +299,7 @@ sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
* is what you poke into the framebuffer to produce the
* colour you requested.
*/
- if (fbi->cmap_inverse) {
+ if (fbi->inf->cmap_inverse) {
red = 0xffff - red;
green = 0xffff - green;
blue = 0xffff - blue;
@@ -607,14 +373,14 @@ sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
var->xres = MIN_XRES;
if (var->yres < MIN_YRES)
var->yres = MIN_YRES;
- if (var->xres > fbi->max_xres)
- var->xres = fbi->max_xres;
- if (var->yres > fbi->max_yres)
- var->yres = fbi->max_yres;
+ if (var->xres > fbi->inf->xres)
+ var->xres = fbi->inf->xres;
+ if (var->yres > fbi->inf->yres)
+ var->yres = fbi->inf->yres;
var->xres_virtual = max(var->xres_virtual, var->xres);
var->yres_virtual = max(var->yres_virtual, var->yres);
- DPRINTK("var->bits_per_pixel=%d\n", var->bits_per_pixel);
+ dev_dbg(fbi->dev, "var->bits_per_pixel=%d\n", var->bits_per_pixel);
switch (var->bits_per_pixel) {
case 4:
rgbidx = RGB_4;
@@ -638,16 +404,16 @@ sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
var->blue = fbi->rgb[rgbidx]->blue;
var->transp = fbi->rgb[rgbidx]->transp;
- DPRINTK("RGBT length = %d:%d:%d:%d\n",
+ dev_dbg(fbi->dev, "RGBT length = %d:%d:%d:%d\n",
var->red.length, var->green.length, var->blue.length,
var->transp.length);
- DPRINTK("RGBT offset = %d:%d:%d:%d\n",
+ dev_dbg(fbi->dev, "RGBT offset = %d:%d:%d:%d\n",
var->red.offset, var->green.offset, var->blue.offset,
var->transp.offset);
#ifdef CONFIG_CPU_FREQ
- printk(KERN_DEBUG "dma period = %d ps, clock = %d kHz\n",
+ dev_dbg(fbi->dev, "dma period = %d ps, clock = %d kHz\n",
sa1100fb_display_dma_period(var),
cpufreq_get(smp_processor_id()));
#endif
@@ -655,22 +421,10 @@ sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
return 0;
}
-static inline void sa1100fb_set_truecolor(u_int is_true_color)
+static void sa1100fb_set_visual(struct sa1100fb_info *fbi, u32 visual)
{
- if (machine_is_assabet()) {
-#if 1 // phase 4 or newer Assabet's
- if (is_true_color)
- ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB);
- else
- ASSABET_BCR_clear(ASSABET_BCR_LCD_12RGB);
-#else
- // older Assabet's
- if (is_true_color)
- ASSABET_BCR_clear(ASSABET_BCR_LCD_12RGB);
- else
- ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB);
-#endif
- }
+ if (fbi->inf->set_visual)
+ fbi->inf->set_visual(visual);
}
/*
@@ -683,11 +437,11 @@ static int sa1100fb_set_par(struct fb_info *info)
struct fb_var_screeninfo *var = &info->var;
unsigned long palette_mem_size;
- DPRINTK("set_par\n");
+ dev_dbg(fbi->dev, "set_par\n");
if (var->bits_per_pixel == 16)
fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR;
- else if (!fbi->cmap_static)
+ else if (!fbi->inf->cmap_static)
fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
else {
/*
@@ -704,7 +458,7 @@ static int sa1100fb_set_par(struct fb_info *info)
palette_mem_size = fbi->palette_size * sizeof(u16);
- DPRINTK("palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size);
+ dev_dbg(fbi->dev, "palette_mem_size = 0x%08lx\n", palette_mem_size);
fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);
fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size;
@@ -712,7 +466,7 @@ static int sa1100fb_set_par(struct fb_info *info)
/*
* Set (any) board control register to handle new color depth
*/
- sa1100fb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR);
+ sa1100fb_set_visual(fbi, fbi->fb.fix.visual);
sa1100fb_activate_var(var, fbi);
return 0;
@@ -728,7 +482,7 @@ sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
/*
* Make sure the user isn't doing something stupid.
*/
- if (!kspc && (fbi->fb.var.bits_per_pixel == 16 || fbi->cmap_static))
+ if (!kspc && (fbi->fb.var.bits_per_pixel == 16 || fbi->inf->cmap_static))
return -EINVAL;
return gen_set_cmap(cmap, kspc, con, info);
@@ -775,7 +529,7 @@ static int sa1100fb_blank(int blank, struct fb_info *info)
struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
int i;
- DPRINTK("sa1100fb_blank: blank=%d\n", blank);
+ dev_dbg(fbi->dev, "sa1100fb_blank: blank=%d\n", blank);
switch (blank) {
case FB_BLANK_POWERDOWN:
@@ -863,43 +617,43 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_
u_int half_screen_size, yres, pcd;
u_long flags;
- DPRINTK("Configuring SA1100 LCD\n");
+ dev_dbg(fbi->dev, "Configuring SA1100 LCD\n");
- DPRINTK("var: xres=%d hslen=%d lm=%d rm=%d\n",
+ dev_dbg(fbi->dev, "var: xres=%d hslen=%d lm=%d rm=%d\n",
var->xres, var->hsync_len,
var->left_margin, var->right_margin);
- DPRINTK("var: yres=%d vslen=%d um=%d bm=%d\n",
+ dev_dbg(fbi->dev, "var: yres=%d vslen=%d um=%d bm=%d\n",
var->yres, var->vsync_len,
var->upper_margin, var->lower_margin);
#if DEBUG_VAR
if (var->xres < 16 || var->xres > 1024)
- printk(KERN_ERR "%s: invalid xres %d\n",
+ dev_err(fbi->dev, "%s: invalid xres %d\n",
fbi->fb.fix.id, var->xres);
if (var->hsync_len < 1 || var->hsync_len > 64)
- printk(KERN_ERR "%s: invalid hsync_len %d\n",
+ dev_err(fbi->dev, "%s: invalid hsync_len %d\n",
fbi->fb.fix.id, var->hsync_len);
if (var->left_margin < 1 || var->left_margin > 255)
- printk(KERN_ERR "%s: invalid left_margin %d\n",
+ dev_err(fbi->dev, "%s: invalid left_margin %d\n",
fbi->fb.fix.id, var->left_margin);
if (var->right_margin < 1 || var->right_margin > 255)
- printk(KERN_ERR "%s: invalid right_margin %d\n",
+ dev_err(fbi->dev, "%s: invalid right_margin %d\n",
fbi->fb.fix.id, var->right_margin);
if (var->yres < 1 || var->yres > 1024)
- printk(KERN_ERR "%s: invalid yres %d\n",
+ dev_err(fbi->dev, "%s: invalid yres %d\n",
fbi->fb.fix.id, var->yres);
if (var->vsync_len < 1 || var->vsync_len > 64)
- printk(KERN_ERR "%s: invalid vsync_len %d\n",
+ dev_err(fbi->dev, "%s: invalid vsync_len %d\n",
fbi->fb.fix.id, var->vsync_len);
if (var->upper_margin < 0 || var->upper_margin > 255)
- printk(KERN_ERR "%s: invalid upper_margin %d\n",
+ dev_err(fbi->dev, "%s: invalid upper_margin %d\n",
fbi->fb.fix.id, var->upper_margin);
if (var->lower_margin < 0 || var->lower_margin > 255)
- printk(KERN_ERR "%s: invalid lower_margin %d\n",
+ dev_err(fbi->dev, "%s: invalid lower_margin %d\n",
fbi->fb.fix.id, var->lower_margin);
#endif
- new_regs.lccr0 = fbi->lccr0 |
+ new_regs.lccr0 = fbi->inf->lccr0 |
LCCR0_LEN | LCCR0_LDM | LCCR0_BAM |
LCCR0_ERM | LCCR0_LtlEnd | LCCR0_DMADel(0);
@@ -914,7 +668,7 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_
* the YRES parameter.
*/
yres = var->yres;
- if (fbi->lccr0 & LCCR0_Dual)
+ if (fbi->inf->lccr0 & LCCR0_Dual)
yres /= 2;
new_regs.lccr2 =
@@ -924,14 +678,14 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_
LCCR2_EndFrmDel(var->lower_margin);
pcd = get_pcd(var->pixclock, cpufreq_get(0));
- new_regs.lccr3 = LCCR3_PixClkDiv(pcd) | fbi->lccr3 |
+ new_regs.lccr3 = LCCR3_PixClkDiv(pcd) | fbi->inf->lccr3 |
(var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) |
(var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL);
- DPRINTK("nlccr0 = 0x%08lx\n", new_regs.lccr0);
- DPRINTK("nlccr1 = 0x%08lx\n", new_regs.lccr1);
- DPRINTK("nlccr2 = 0x%08lx\n", new_regs.lccr2);
- DPRINTK("nlccr3 = 0x%08lx\n", new_regs.lccr3);
+ dev_dbg(fbi->dev, "nlccr0 = 0x%08lx\n", new_regs.lccr0);
+ dev_dbg(fbi->dev, "nlccr1 = 0x%08lx\n", new_regs.lccr1);
+ dev_dbg(fbi->dev, "nlccr2 = 0x%08lx\n", new_regs.lccr2);
+ dev_dbg(fbi->dev, "nlccr3 = 0x%08lx\n", new_regs.lccr3);
half_screen_size = var->bits_per_pixel;
half_screen_size = half_screen_size * var->xres * var->yres / 16;
@@ -951,9 +705,12 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_
* Only update the registers if the controller is enabled
* and something has changed.
*/
- if ((LCCR0 != fbi->reg_lccr0) || (LCCR1 != fbi->reg_lccr1) ||
- (LCCR2 != fbi->reg_lccr2) || (LCCR3 != fbi->reg_lccr3) ||
- (DBAR1 != fbi->dbar1) || (DBAR2 != fbi->dbar2))
+ if (readl_relaxed(fbi->base + LCCR0) != fbi->reg_lccr0 ||
+ readl_relaxed(fbi->base + LCCR1) != fbi->reg_lccr1 ||
+ readl_relaxed(fbi->base + LCCR2) != fbi->reg_lccr2 ||
+ readl_relaxed(fbi->base + LCCR3) != fbi->reg_lccr3 ||
+ readl_relaxed(fbi->base + DBAR1) != fbi->dbar1 ||
+ readl_relaxed(fbi->base + DBAR2) != fbi->dbar2)
sa1100fb_schedule_work(fbi, C_REENABLE);
return 0;
@@ -967,18 +724,18 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_
*/
static inline void __sa1100fb_backlight_power(struct sa1100fb_info *fbi, int on)
{
- DPRINTK("backlight o%s\n", on ? "n" : "ff");
+ dev_dbg(fbi->dev, "backlight o%s\n", on ? "n" : "ff");
- if (sa1100fb_backlight_power)
- sa1100fb_backlight_power(on);
+ if (fbi->inf->backlight_power)
+ fbi->inf->backlight_power(on);
}
static inline void __sa1100fb_lcd_power(struct sa1100fb_info *fbi, int on)
{
- DPRINTK("LCD power o%s\n", on ? "n" : "ff");
+ dev_dbg(fbi->dev, "LCD power o%s\n", on ? "n" : "ff");
- if (sa1100fb_lcd_power)
- sa1100fb_lcd_power(on);
+ if (fbi->inf->lcd_power)
+ fbi->inf->lcd_power(on);
}
static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi)
@@ -1008,14 +765,25 @@ static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi)
}
if (mask) {
+ unsigned long flags;
+
+ /*
+ * SA-1100 requires the GPIO direction register set
+ * appropriately for the alternate function. Hence
+ * we set it here via bitmask rather than excessive
+ * fiddling via the GPIO subsystem - and even then
+ * we'll still have to deal with GAFR.
+ */
+ local_irq_save(flags);
GPDR |= mask;
GAFR |= mask;
+ local_irq_restore(flags);
}
}
static void sa1100fb_enable_controller(struct sa1100fb_info *fbi)
{
- DPRINTK("Enabling LCD controller\n");
+ dev_dbg(fbi->dev, "Enabling LCD controller\n");
/*
* Make sure the mode bits are present in the first palette entry
@@ -1024,43 +792,46 @@ static void sa1100fb_enable_controller(struct sa1100fb_info *fbi)
fbi->palette_cpu[0] |= palette_pbs(&fbi->fb.var);
/* Sequence from 11.7.10 */
- LCCR3 = fbi->reg_lccr3;
- LCCR2 = fbi->reg_lccr2;
- LCCR1 = fbi->reg_lccr1;
- LCCR0 = fbi->reg_lccr0 & ~LCCR0_LEN;
- DBAR1 = fbi->dbar1;
- DBAR2 = fbi->dbar2;
- LCCR0 |= LCCR0_LEN;
-
- if (machine_is_shannon()) {
- GPDR |= SHANNON_GPIO_DISP_EN;
- GPSR |= SHANNON_GPIO_DISP_EN;
- }
-
- DPRINTK("DBAR1 = 0x%08x\n", DBAR1);
- DPRINTK("DBAR2 = 0x%08x\n", DBAR2);
- DPRINTK("LCCR0 = 0x%08x\n", LCCR0);
- DPRINTK("LCCR1 = 0x%08x\n", LCCR1);
- DPRINTK("LCCR2 = 0x%08x\n", LCCR2);
- DPRINTK("LCCR3 = 0x%08x\n", LCCR3);
+ writel_relaxed(fbi->reg_lccr3, fbi->base + LCCR3);
+ writel_relaxed(fbi->reg_lccr2, fbi->base + LCCR2);
+ writel_relaxed(fbi->reg_lccr1, fbi->base + LCCR1);
+ writel_relaxed(fbi->reg_lccr0 & ~LCCR0_LEN, fbi->base + LCCR0);
+ writel_relaxed(fbi->dbar1, fbi->base + DBAR1);
+ writel_relaxed(fbi->dbar2, fbi->base + DBAR2);
+ writel_relaxed(fbi->reg_lccr0 | LCCR0_LEN, fbi->base + LCCR0);
+
+ if (machine_is_shannon())
+ gpio_set_value(SHANNON_GPIO_DISP_EN, 1);
+
+ dev_dbg(fbi->dev, "DBAR1: 0x%08x\n", readl_relaxed(fbi->base + DBAR1));
+ dev_dbg(fbi->dev, "DBAR2: 0x%08x\n", readl_relaxed(fbi->base + DBAR2));
+ dev_dbg(fbi->dev, "LCCR0: 0x%08x\n", readl_relaxed(fbi->base + LCCR0));
+ dev_dbg(fbi->dev, "LCCR1: 0x%08x\n", readl_relaxed(fbi->base + LCCR1));
+ dev_dbg(fbi->dev, "LCCR2: 0x%08x\n", readl_relaxed(fbi->base + LCCR2));
+ dev_dbg(fbi->dev, "LCCR3: 0x%08x\n", readl_relaxed(fbi->base + LCCR3));
}
static void sa1100fb_disable_controller(struct sa1100fb_info *fbi)
{
DECLARE_WAITQUEUE(wait, current);
+ u32 lccr0;
- DPRINTK("Disabling LCD controller\n");
+ dev_dbg(fbi->dev, "Disabling LCD controller\n");
- if (machine_is_shannon()) {
- GPCR |= SHANNON_GPIO_DISP_EN;
- }
+ if (machine_is_shannon())
+ gpio_set_value(SHANNON_GPIO_DISP_EN, 0);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&fbi->ctrlr_wait, &wait);
- LCSR = 0xffffffff; /* Clear LCD Status Register */
- LCCR0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */
- LCCR0 &= ~LCCR0_LEN; /* Disable LCD Controller */
+ /* Clear LCD Status Register */
+ writel_relaxed(~0, fbi->base + LCSR);
+
+ lccr0 = readl_relaxed(fbi->base + LCCR0);
+ lccr0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */
+ writel_relaxed(lccr0, fbi->base + LCCR0);
+ lccr0 &= ~LCCR0_LEN; /* Disable LCD Controller */
+ writel_relaxed(lccr0, fbi->base + LCCR0);
schedule_timeout(20 * HZ / 1000);
remove_wait_queue(&fbi->ctrlr_wait, &wait);
@@ -1072,14 +843,15 @@ static void sa1100fb_disable_controller(struct sa1100fb_info *fbi)
static irqreturn_t sa1100fb_handle_irq(int irq, void *dev_id)
{
struct sa1100fb_info *fbi = dev_id;
- unsigned int lcsr = LCSR;
+ unsigned int lcsr = readl_relaxed(fbi->base + LCSR);
if (lcsr & LCSR_LDD) {
- LCCR0 |= LCCR0_LDM;
+ u32 lccr0 = readl_relaxed(fbi->base + LCCR0) | LCCR0_LDM;
+ writel_relaxed(lccr0, fbi->base + LCCR0);
wake_up(&fbi->ctrlr_wait);
}
- LCSR = lcsr;
+ writel_relaxed(lcsr, fbi->base + LCSR);
return IRQ_HANDLED;
}
@@ -1268,7 +1040,7 @@ sa1100fb_freq_policy(struct notifier_block *nb, unsigned long val,
switch (val) {
case CPUFREQ_ADJUST:
case CPUFREQ_INCOMPATIBLE:
- printk(KERN_DEBUG "min dma period: %d ps, "
+ dev_dbg(fbi->dev, "min dma period: %d ps, "
"new clock %d kHz\n", sa1100fb_min_dma_period(fbi),
policy->max);
/* todo: fill in min/max values */
@@ -1318,7 +1090,7 @@ static int sa1100fb_resume(struct platform_device *dev)
* cache. Once this area is remapped, all virtual memory
* access to the video memory should occur at the new region.
*/
-static int __init sa1100fb_map_video_memory(struct sa1100fb_info *fbi)
+static int __devinit sa1100fb_map_video_memory(struct sa1100fb_info *fbi)
{
/*
* We reserve one page for the palette, plus the size
@@ -1344,7 +1116,7 @@ static int __init sa1100fb_map_video_memory(struct sa1100fb_info *fbi)
}
/* Fake monspecs to fill in fbinfo structure */
-static struct fb_monspecs monspecs __initdata = {
+static struct fb_monspecs monspecs __devinitdata = {
.hfmin = 30000,
.hfmax = 70000,
.vfmin = 50,
@@ -1352,10 +1124,11 @@ static struct fb_monspecs monspecs __initdata = {
};
-static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev)
+static struct sa1100fb_info * __devinit sa1100fb_init_fbinfo(struct device *dev)
{
- struct sa1100fb_mach_info *inf;
+ struct sa1100fb_mach_info *inf = dev->platform_data;
struct sa1100fb_info *fbi;
+ unsigned i;
fbi = kmalloc(sizeof(struct sa1100fb_info) + sizeof(u32) * 16,
GFP_KERNEL);
@@ -1390,8 +1163,6 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev)
fbi->rgb[RGB_8] = &rgb_8;
fbi->rgb[RGB_16] = &def_rgb_16;
- inf = sa1100fb_get_machine_info(fbi);
-
/*
* People just don't seem to get this. We don't support
* anything but correct entries now, so panic if someone
@@ -1402,13 +1173,10 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev)
panic("sa1100fb error: invalid LCCR3 fields set or zero "
"pixclock.");
- fbi->max_xres = inf->xres;
fbi->fb.var.xres = inf->xres;
fbi->fb.var.xres_virtual = inf->xres;
- fbi->max_yres = inf->yres;
fbi->fb.var.yres = inf->yres;
fbi->fb.var.yres_virtual = inf->yres;
- fbi->max_bpp = inf->bpp;
fbi->fb.var.bits_per_pixel = inf->bpp;
fbi->fb.var.pixclock = inf->pixclock;
fbi->fb.var.hsync_len = inf->hsync_len;
@@ -1419,14 +1187,16 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev)
fbi->fb.var.lower_margin = inf->lower_margin;
fbi->fb.var.sync = inf->sync;
fbi->fb.var.grayscale = inf->cmap_greyscale;
- fbi->cmap_inverse = inf->cmap_inverse;
- fbi->cmap_static = inf->cmap_static;
- fbi->lccr0 = inf->lccr0;
- fbi->lccr3 = inf->lccr3;
fbi->state = C_STARTUP;
fbi->task_state = (u_char)-1;
- fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres *
- fbi->max_bpp / 8;
+ fbi->fb.fix.smem_len = inf->xres * inf->yres *
+ inf->bpp / 8;
+ fbi->inf = inf;
+
+ /* Copy the RGB bitfield overrides */
+ for (i = 0; i < NR_RGB; i++)
+ if (inf->rgb[i])
+ fbi->rgb[i] = inf->rgb[i];
init_waitqueue_head(&fbi->ctrlr_wait);
INIT_WORK(&fbi->task, sa1100fb_task);
@@ -1438,13 +1208,20 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev)
static int __devinit sa1100fb_probe(struct platform_device *pdev)
{
struct sa1100fb_info *fbi;
+ struct resource *res;
int ret, irq;
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "no platform LCD data\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
- if (irq < 0)
+ if (irq < 0 || !res)
return -EINVAL;
- if (!request_mem_region(0xb0100000, 0x10000, "LCD"))
+ if (!request_mem_region(res->start, resource_size(res), "LCD"))
return -EBUSY;
fbi = sa1100fb_init_fbinfo(&pdev->dev);
@@ -1452,6 +1229,10 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev)
if (!fbi)
goto failed;
+ fbi->base = ioremap(res->start, resource_size(res));
+ if (!fbi->base)
+ goto failed;
+
/* Initialize video memory */
ret = sa1100fb_map_video_memory(fbi);
if (ret)
@@ -1459,14 +1240,16 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev)
ret = request_irq(irq, sa1100fb_handle_irq, 0, "LCD", fbi);
if (ret) {
- printk(KERN_ERR "sa1100fb: request_irq failed: %d\n", ret);
+ dev_err(&pdev->dev, "request_irq failed: %d\n", ret);
goto failed;
}
-#ifdef ASSABET_PAL_VIDEO
- if (machine_is_assabet())
- ASSABET_BCR_clear(ASSABET_BCR_LCD_ON);
-#endif
+ if (machine_is_shannon()) {
+ ret = gpio_request_one(SHANNON_GPIO_DISP_EN,
+ GPIOF_OUT_INIT_LOW, "display enable");
+ if (ret)
+ goto err_free_irq;
+ }
/*
* This makes sure that our colour bitfield
@@ -1478,7 +1261,7 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev)
ret = register_framebuffer(&fbi->fb);
if (ret < 0)
- goto err_free_irq;
+ goto err_reg_fb;
#ifdef CONFIG_CPU_FREQ
fbi->freq_transition.notifier_call = sa1100fb_freq_transition;
@@ -1490,12 +1273,17 @@ static int __devinit sa1100fb_probe(struct platform_device *pdev)
/* This driver cannot be unloaded at the moment */
return 0;
+ err_reg_fb:
+ if (machine_is_shannon())
+ gpio_free(SHANNON_GPIO_DISP_EN);
err_free_irq:
free_irq(irq, fbi);
failed:
+ if (fbi)
+ iounmap(fbi->base);
platform_set_drvdata(pdev, NULL);
kfree(fbi);
- release_mem_region(0xb0100000, 0x10000);
+ release_mem_region(res->start, resource_size(res));
return ret;
}
@@ -1505,6 +1293,7 @@ static struct platform_driver sa1100fb_driver = {
.resume = sa1100fb_resume,
.driver = {
.name = "sa11x0-fb",
+ .owner = THIS_MODULE,
},
};
diff --git a/drivers/video/sa1100fb.h b/drivers/video/sa1100fb.h
index 1c3b459..fc5d429 100644
--- a/drivers/video/sa1100fb.h
+++ b/drivers/video/sa1100fb.h
@@ -10,44 +10,15 @@
* for more details.
*/
-/*
- * These are the bitfields for each
- * display depth that we support.
- */
-struct sa1100fb_rgb {
- struct fb_bitfield red;
- struct fb_bitfield green;
- struct fb_bitfield blue;
- struct fb_bitfield transp;
-};
-
-/*
- * This structure describes the machine which we are running on.
- */
-struct sa1100fb_mach_info {
- u_long pixclock;
-
- u_short xres;
- u_short yres;
-
- u_char bpp;
- u_char hsync_len;
- u_char left_margin;
- u_char right_margin;
-
- u_char vsync_len;
- u_char upper_margin;
- u_char lower_margin;
- u_char sync;
-
- u_int cmap_greyscale:1,
- cmap_inverse:1,
- cmap_static:1,
- unused:29;
-
- u_int lccr0;
- u_int lccr3;
-};
+#define LCCR0 0x0000 /* LCD Control Reg. 0 */
+#define LCSR 0x0004 /* LCD Status Reg. */
+#define DBAR1 0x0010 /* LCD DMA Base Address Reg. channel 1 */
+#define DCAR1 0x0014 /* LCD DMA Current Address Reg. channel 1 */
+#define DBAR2 0x0018 /* LCD DMA Base Address Reg. channel 2 */
+#define DCAR2 0x001C /* LCD DMA Current Address Reg. channel 2 */
+#define LCCR1 0x0020 /* LCD Control Reg. 1 */
+#define LCCR2 0x0024 /* LCD Control Reg. 2 */
+#define LCCR3 0x0028 /* LCD Control Reg. 3 */
/* Shadows for LCD controller registers */
struct sa1100fb_lcd_reg {
@@ -57,19 +28,11 @@ struct sa1100fb_lcd_reg {
unsigned long lccr3;
};
-#define RGB_4 (0)
-#define RGB_8 (1)
-#define RGB_16 (2)
-#define NR_RGB 3
-
struct sa1100fb_info {
struct fb_info fb;
struct device *dev;
- struct sa1100fb_rgb *rgb[NR_RGB];
-
- u_int max_bpp;
- u_int max_xres;
- u_int max_yres;
+ const struct sa1100fb_rgb *rgb[NR_RGB];
+ void __iomem *base;
/*
* These are the addresses we mapped
@@ -88,12 +51,6 @@ struct sa1100fb_info {
dma_addr_t dbar1;
dma_addr_t dbar2;
- u_int lccr0;
- u_int lccr3;
- u_int cmap_inverse:1,
- cmap_static:1,
- unused:30;
-
u_int reg_lccr0;
u_int reg_lccr1;
u_int reg_lccr2;
@@ -109,6 +66,8 @@ struct sa1100fb_info {
struct notifier_block freq_transition;
struct notifier_block freq_policy;
#endif
+
+ const struct sa1100fb_mach_info *inf;
};
#define TO_INF(ptr,member) container_of(ptr,struct sa1100fb_info,member)
@@ -130,15 +89,6 @@ struct sa1100fb_info {
#define SA1100_NAME "SA1100"
/*
- * Debug macros
- */
-#if DEBUG
-# define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ## args)
-#else
-# define DPRINTK(fmt, args...)
-#endif
-
-/*
* Minimum X and Y resolutions
*/
#define MIN_XRES 64
diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c
index beb4950..cee7803 100644
--- a/drivers/video/savage/savagefb_driver.c
+++ b/drivers/video/savage/savagefb_driver.c
@@ -56,7 +56,6 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pgtable.h>
-#include <asm/system.h>
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
diff --git a/drivers/virtio/config.c b/drivers/virtio/config.c
index 983d482..f70bcd2 100644
--- a/drivers/virtio/config.c
+++ b/drivers/virtio/config.c
@@ -9,5 +9,4 @@
#include <linux/virtio.h>
#include <linux/virtio_config.h>
#include <linux/bug.h>
-#include <asm/system.h>
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 958e512..05f0a80 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -398,21 +398,8 @@ static int restore_common(struct virtio_device *vdev)
return 0;
}
-static int virtballoon_thaw(struct virtio_device *vdev)
-{
- return restore_common(vdev);
-}
-
static int virtballoon_restore(struct virtio_device *vdev)
{
- struct virtio_balloon *vb = vdev->priv;
-
- /*
- * If a request wasn't complete at the time of freezing, this
- * could have been set.
- */
- vb->need_stats_update = 0;
-
return restore_common(vdev);
}
#endif
@@ -434,7 +421,6 @@ static struct virtio_driver virtio_balloon_driver = {
#ifdef CONFIG_PM
.freeze = virtballoon_freeze,
.restore = virtballoon_restore,
- .thaw = virtballoon_thaw,
#endif
};
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index 635e1ef..2e03d41 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -720,24 +720,6 @@ static void __devexit virtio_pci_remove(struct pci_dev *pci_dev)
}
#ifdef CONFIG_PM
-static int virtio_pci_suspend(struct device *dev)
-{
- struct pci_dev *pci_dev = to_pci_dev(dev);
-
- pci_save_state(pci_dev);
- pci_set_power_state(pci_dev, PCI_D3hot);
- return 0;
-}
-
-static int virtio_pci_resume(struct device *dev)
-{
- struct pci_dev *pci_dev = to_pci_dev(dev);
-
- pci_restore_state(pci_dev);
- pci_set_power_state(pci_dev, PCI_D0);
- return 0;
-}
-
static int virtio_pci_freeze(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -758,59 +740,24 @@ static int virtio_pci_freeze(struct device *dev)
return ret;
}
-static int restore_common(struct device *dev)
-{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
- int ret;
-
- ret = pci_enable_device(pci_dev);
- if (ret)
- return ret;
- pci_set_master(pci_dev);
- vp_finalize_features(&vp_dev->vdev);
-
- return ret;
-}
-
-static int virtio_pci_thaw(struct device *dev)
+static int virtio_pci_restore(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
struct virtio_driver *drv;
int ret;
- ret = restore_common(dev);
- if (ret)
- return ret;
-
drv = container_of(vp_dev->vdev.dev.driver,
struct virtio_driver, driver);
- if (drv && drv->thaw)
- ret = drv->thaw(&vp_dev->vdev);
- else if (drv && drv->restore)
- ret = drv->restore(&vp_dev->vdev);
-
- /* Finally, tell the device we're all set */
- if (!ret)
- vp_set_status(&vp_dev->vdev, vp_dev->saved_status);
-
- return ret;
-}
-
-static int virtio_pci_restore(struct device *dev)
-{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
- struct virtio_driver *drv;
- int ret;
+ ret = pci_enable_device(pci_dev);
+ if (ret)
+ return ret;
- drv = container_of(vp_dev->vdev.dev.driver,
- struct virtio_driver, driver);
+ pci_set_master(pci_dev);
+ vp_finalize_features(&vp_dev->vdev);
- ret = restore_common(dev);
- if (!ret && drv && drv->restore)
+ if (drv && drv->restore)
ret = drv->restore(&vp_dev->vdev);
/* Finally, tell the device we're all set */
@@ -821,12 +768,7 @@ static int virtio_pci_restore(struct device *dev)
}
static const struct dev_pm_ops virtio_pci_pm_ops = {
- .suspend = virtio_pci_suspend,
- .resume = virtio_pci_resume,
- .freeze = virtio_pci_freeze,
- .thaw = virtio_pci_thaw,
- .restore = virtio_pci_restore,
- .poweroff = virtio_pci_suspend,
+ SET_SYSTEM_SLEEP_PM_OPS(virtio_pci_freeze, virtio_pci_restore)
};
#endif
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 0e7366d..3709624 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -55,6 +55,7 @@ comment "Watchdog Device Drivers"
config SOFT_WATCHDOG
tristate "Software watchdog"
+ select WATCHDOG_CORE
help
A software monitoring watchdog. This will fail to reboot your system
from some situations that the hardware watchdog will recover
@@ -74,6 +75,7 @@ config WM831X_WATCHDOG
config WM8350_WATCHDOG
tristate "WM8350 watchdog"
depends on MFD_WM8350
+ select WATCHDOG_CORE
help
Support for the watchdog in the WM8350 AudioPlus PMIC. When
the watchdog triggers the system will be reset.
@@ -217,6 +219,7 @@ config MPCORE_WATCHDOG
config EP93XX_WATCHDOG
tristate "EP93xx Watchdog"
depends on ARCH_EP93XX
+ select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
embedded in the Cirrus Logic EP93xx family of devices.
@@ -234,6 +237,7 @@ config OMAP_WATCHDOG
config PNX4008_WATCHDOG
tristate "PNX4008 and LPC32XX Watchdog"
depends on ARCH_PNX4008 || ARCH_LPC32XX
+ select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
in the PNX4008 or LPC32XX processor.
@@ -283,6 +287,7 @@ config COH901327_WATCHDOG
bool "ST-Ericsson COH 901 327 watchdog"
depends on ARCH_U300
default y if MACH_U300
+ select WATCHDOG_CORE
help
Say Y here to include Watchdog timer support for the
watchdog embedded into the ST-Ericsson U300 series platforms.
@@ -328,6 +333,7 @@ config TS72XX_WATCHDOG
config MAX63XX_WATCHDOG
tristate "Max63xx watchdog"
depends on ARM && HAS_IOMEM
+ select WATCHDOG_CORE
help
Support for memory mapped max63{69,70,71,72,73,74} watchdog timer.
@@ -955,6 +961,7 @@ config INDYDOG
config JZ4740_WDT
tristate "Ingenic jz4740 SoC hardware watchdog"
depends on MACH_JZ4740
+ select WATCHDOG_CORE
help
Hardware driver for the built-in watchdog timer on Ingenic jz4740 SoCs.
@@ -996,6 +1003,7 @@ config AR7_WDT
config TXX9_WDT
tristate "Toshiba TXx9 Watchdog Timer"
depends on CPU_TX39XX || CPU_TX49XX
+ select WATCHDOG_CORE
help
Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs.
diff --git a/drivers/watchdog/acquirewdt.c b/drivers/watchdog/acquirewdt.c
index b6a2b58..4397881 100644
--- a/drivers/watchdog/acquirewdt.c
+++ b/drivers/watchdog/acquirewdt.c
@@ -52,6 +52,8 @@
* Includes, defines, variables, module parameters, ...
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
/* Includes */
#include <linux/module.h> /* For module specific items */
#include <linux/moduleparam.h> /* For new moduleparam's */
@@ -70,7 +72,6 @@
/* Module information */
#define DRV_NAME "acquirewdt"
-#define PFX DRV_NAME ": "
#define WATCHDOG_NAME "Acquire WDT"
/* There is no way to see what the correct time-out period is */
#define WATCHDOG_HEARTBEAT 0
@@ -92,8 +93,8 @@ static int wdt_start = 0x443;
module_param(wdt_start, int, 0);
MODULE_PARM_DESC(wdt_start, "Acquire WDT 'start' io port (default 0x443)");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -208,8 +209,7 @@ static int acq_close(struct inode *inode, struct file *file)
if (expect_close == 42) {
acq_stop();
} else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
acq_keepalive();
}
clear_bit(0, &acq_is_open);
@@ -246,27 +246,24 @@ static int __devinit acq_probe(struct platform_device *dev)
if (wdt_stop != wdt_start) {
if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) {
- printk(KERN_ERR PFX
- "I/O address 0x%04x already in use\n", wdt_stop);
+ pr_err("I/O address 0x%04x already in use\n", wdt_stop);
ret = -EIO;
goto out;
}
}
if (!request_region(wdt_start, 1, WATCHDOG_NAME)) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
- wdt_start);
+ pr_err("I/O address 0x%04x already in use\n", wdt_start);
ret = -EIO;
goto unreg_stop;
}
ret = misc_register(&acq_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto unreg_regions;
}
- printk(KERN_INFO PFX "initialized. (nowayout=%d)\n", nowayout);
+ pr_info("initialized. (nowayout=%d)\n", nowayout);
return 0;
unreg_regions:
@@ -308,8 +305,7 @@ static int __init acq_init(void)
{
int err;
- printk(KERN_INFO
- "WDT driver for Acquire single board computer initialising.\n");
+ pr_info("WDT driver for Acquire single board computer initialising\n");
err = platform_driver_register(&acquirewdt_driver);
if (err)
@@ -332,7 +328,7 @@ static void __exit acq_exit(void)
{
platform_device_unregister(acq_platform_device);
platform_driver_unregister(&acquirewdt_driver);
- printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+ pr_info("Watchdog Module Unloaded\n");
}
module_init(acq_init);
diff --git a/drivers/watchdog/advantechwdt.c b/drivers/watchdog/advantechwdt.c
index 4d40965..64ae9e9 100644
--- a/drivers/watchdog/advantechwdt.c
+++ b/drivers/watchdog/advantechwdt.c
@@ -28,6 +28,8 @@
* add wdt_start and wdt_stop as parameters.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -40,10 +42,8 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#define DRV_NAME "advantechwdt"
-#define PFX DRV_NAME ": "
#define WATCHDOG_NAME "Advantech WDT"
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
@@ -77,8 +77,8 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. 1<= timeout <=63, default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -207,8 +207,7 @@ static int advwdt_close(struct inode *inode, struct file *file)
if (adv_expect_close == 42) {
advwdt_disable();
} else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
advwdt_ping();
}
clear_bit(0, &advwdt_is_open);
@@ -245,18 +244,15 @@ static int __devinit advwdt_probe(struct platform_device *dev)
if (wdt_stop != wdt_start) {
if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) {
- printk(KERN_ERR PFX
- "I/O address 0x%04x already in use\n",
- wdt_stop);
+ pr_err("I/O address 0x%04x already in use\n",
+ wdt_stop);
ret = -EIO;
goto out;
}
}
if (!request_region(wdt_start, 1, WATCHDOG_NAME)) {
- printk(KERN_ERR PFX
- "I/O address 0x%04x already in use\n",
- wdt_start);
+ pr_err("I/O address 0x%04x already in use\n", wdt_start);
ret = -EIO;
goto unreg_stop;
}
@@ -265,18 +261,16 @@ static int __devinit advwdt_probe(struct platform_device *dev)
* if not reset to the default */
if (advwdt_set_heartbeat(timeout)) {
advwdt_set_heartbeat(WATCHDOG_TIMEOUT);
- printk(KERN_INFO PFX
- "timeout value must be 1<=x<=63, using %d\n", timeout);
+ pr_info("timeout value must be 1<=x<=63, using %d\n", timeout);
}
ret = misc_register(&advwdt_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto unreg_regions;
}
- printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
+ pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
timeout, nowayout);
out:
return ret;
@@ -318,8 +312,7 @@ static int __init advwdt_init(void)
{
int err;
- printk(KERN_INFO
- "WDT driver for Advantech single board computer initialising.\n");
+ pr_info("WDT driver for Advantech single board computer initialising\n");
err = platform_driver_register(&advwdt_driver);
if (err)
@@ -343,7 +336,7 @@ static void __exit advwdt_exit(void)
{
platform_device_unregister(advwdt_platform_device);
platform_driver_unregister(&advwdt_driver);
- printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+ pr_info("Watchdog Module Unloaded\n");
}
module_init(advwdt_init);
diff --git a/drivers/watchdog/alim1535_wdt.c b/drivers/watchdog/alim1535_wdt.c
index f16dcbd..41b8493 100644
--- a/drivers/watchdog/alim1535_wdt.c
+++ b/drivers/watchdog/alim1535_wdt.c
@@ -7,6 +7,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -22,7 +24,6 @@
#include <linux/io.h>
#define WATCHDOG_NAME "ALi_M1535"
-#define PFX WATCHDOG_NAME ": "
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
/* internal variables */
@@ -39,8 +40,8 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. (0 < timeout < 18000, default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -268,8 +269,7 @@ static int ali_release(struct inode *inode, struct file *file)
if (ali_expect_release == 42)
ali_stop();
else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
ali_keepalive();
}
clear_bit(0, &ali_is_open);
@@ -399,9 +399,8 @@ static int __init watchdog_init(void)
if not reset to the default */
if (timeout < 1 || timeout >= 18000) {
timeout = WATCHDOG_TIMEOUT;
- printk(KERN_INFO PFX
- "timeout value must be 0 < timeout < 18000, using %d\n",
- timeout);
+ pr_info("timeout value must be 0 < timeout < 18000, using %d\n",
+ timeout);
}
/* Calculate the watchdog's timeout */
@@ -409,20 +408,18 @@ static int __init watchdog_init(void)
ret = register_reboot_notifier(&ali_notifier);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
goto out;
}
ret = misc_register(&ali_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto unreg_reboot;
}
- printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
+ pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
timeout, nowayout);
out:
diff --git a/drivers/watchdog/alim7101_wdt.c b/drivers/watchdog/alim7101_wdt.c
index 46f4b85..5eee550 100644
--- a/drivers/watchdog/alim7101_wdt.c
+++ b/drivers/watchdog/alim7101_wdt.c
@@ -19,6 +19,8 @@
* -- Mike Waychison <michael.waychison@sun.com>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -34,10 +36,6 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
-
-#define OUR_NAME "alim7101_wdt"
-#define PFX OUR_NAME ": "
#define WDT_ENABLE 0x9C
#define WDT_DISABLE 0x8C
@@ -79,8 +77,8 @@ static unsigned long wdt_is_open;
static char wdt_expect_close;
static struct pci_dev *alim7101_pmu;
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -112,8 +110,7 @@ static void wdt_timer_ping(unsigned long data)
ALI_7101_GPIO_O, tmp & ~0x20);
}
} else {
- printk(KERN_WARNING PFX
- "Heartbeat lost! Will not ping the watchdog\n");
+ pr_warn("Heartbeat lost! Will not ping the watchdog\n");
}
/* Re-set the timer interval */
mod_timer(&timer, jiffies + WDT_INTERVAL);
@@ -162,7 +159,7 @@ static void wdt_startup(void)
/* Start the timer */
mod_timer(&timer, jiffies + WDT_INTERVAL);
- printk(KERN_INFO PFX "Watchdog timer is now enabled.\n");
+ pr_info("Watchdog timer is now enabled\n");
}
static void wdt_turnoff(void)
@@ -170,7 +167,7 @@ static void wdt_turnoff(void)
/* Stop the timer */
del_timer_sync(&timer);
wdt_change(WDT_DISABLE);
- printk(KERN_INFO PFX "Watchdog timer is now disabled...\n");
+ pr_info("Watchdog timer is now disabled...\n");
}
static void wdt_keepalive(void)
@@ -226,8 +223,7 @@ static int fop_close(struct inode *inode, struct file *file)
wdt_turnoff();
else {
/* wim: shouldn't there be a: del_timer(&timer); */
- printk(KERN_CRIT PFX
- "device file closed unexpectedly. Will not stop the WDT!\n");
+ pr_crit("device file closed unexpectedly. Will not stop the WDT!\n");
}
clear_bit(0, &wdt_is_open);
wdt_expect_close = 0;
@@ -322,8 +318,7 @@ static int wdt_notify_sys(struct notifier_block *this,
* watchdog on reboot with no heartbeat
*/
wdt_change(WDT_ENABLE);
- printk(KERN_INFO PFX "Watchdog timer is now enabled "
- "with no heartbeat - should reboot in ~1 second.\n");
+ pr_info("Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second\n");
}
return NOTIFY_DONE;
}
@@ -352,12 +347,11 @@ static int __init alim7101_wdt_init(void)
struct pci_dev *ali1543_south;
char tmp;
- printk(KERN_INFO PFX "Steve Hill <steve@navaho.co.uk>.\n");
+ pr_info("Steve Hill <steve@navaho.co.uk>\n");
alim7101_pmu = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101,
NULL);
if (!alim7101_pmu) {
- printk(KERN_INFO PFX
- "ALi M7101 PMU not present - WDT not set\n");
+ pr_info("ALi M7101 PMU not present - WDT not set\n");
return -EBUSY;
}
@@ -367,56 +361,46 @@ static int __init alim7101_wdt_init(void)
ali1543_south = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533,
NULL);
if (!ali1543_south) {
- printk(KERN_INFO PFX
- "ALi 1543 South-Bridge not present - WDT not set\n");
+ pr_info("ALi 1543 South-Bridge not present - WDT not set\n");
goto err_out;
}
pci_read_config_byte(ali1543_south, 0x5e, &tmp);
pci_dev_put(ali1543_south);
if ((tmp & 0x1e) == 0x00) {
if (!use_gpio) {
- printk(KERN_INFO PFX
- "Detected old alim7101 revision 'a1d'. "
- "If this is a cobalt board, set the 'use_gpio' "
- "module parameter.\n");
+ pr_info("Detected old alim7101 revision 'a1d'. If this is a cobalt board, set the 'use_gpio' module parameter.\n");
goto err_out;
}
nowayout = 1;
} else if ((tmp & 0x1e) != 0x12 && (tmp & 0x1e) != 0x00) {
- printk(KERN_INFO PFX
- "ALi 1543 South-Bridge does not have the correct "
- "revision number (???1001?) - WDT not set\n");
+ pr_info("ALi 1543 South-Bridge does not have the correct revision number (???1001?) - WDT not set\n");
goto err_out;
}
if (timeout < 1 || timeout > 3600) {
/* arbitrary upper limit */
timeout = WATCHDOG_TIMEOUT;
- printk(KERN_INFO PFX
- "timeout value must be 1 <= x <= 3600, using %d\n",
- timeout);
+ pr_info("timeout value must be 1 <= x <= 3600, using %d\n",
+ timeout);
}
rc = register_reboot_notifier(&wdt_notifier);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", rc);
+ pr_err("cannot register reboot notifier (err=%d)\n", rc);
goto err_out;
}
rc = misc_register(&wdt_miscdev);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- wdt_miscdev.minor, rc);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
goto err_out_reboot;
}
if (nowayout)
__module_get(THIS_MODULE);
- printk(KERN_INFO PFX "WDT driver for ALi M7101 initialised. "
- "timeout=%d sec (nowayout=%d)\n",
+ pr_info("WDT driver for ALi M7101 initialised. timeout=%d sec (nowayout=%d)\n",
timeout, nowayout);
return 0;
diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c
index 502773a..639ae9a 100644
--- a/drivers/watchdog/ar7_wdt.c
+++ b/drivers/watchdog/ar7_wdt.c
@@ -23,6 +23,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
@@ -39,7 +41,6 @@
#include <asm/addrspace.h>
#include <asm/mach-ar7/ar7.h>
-#define DRVNAME "ar7_wdt"
#define LONGNAME "TI AR7 Watchdog Timer"
MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
@@ -51,8 +52,8 @@ static int margin = 60;
module_param(margin, int, 0);
MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
#define READ_REG(x) readl((void __iomem *)&(x))
@@ -93,7 +94,7 @@ static void ar7_wdt_kick(u32 value)
return;
}
}
- printk(KERN_ERR DRVNAME ": failed to unlock WDT kick reg\n");
+ pr_err("failed to unlock WDT kick reg\n");
}
static void ar7_wdt_prescale(u32 value)
@@ -106,7 +107,7 @@ static void ar7_wdt_prescale(u32 value)
return;
}
}
- printk(KERN_ERR DRVNAME ": failed to unlock WDT prescale reg\n");
+ pr_err("failed to unlock WDT prescale reg\n");
}
static void ar7_wdt_change(u32 value)
@@ -119,7 +120,7 @@ static void ar7_wdt_change(u32 value)
return;
}
}
- printk(KERN_ERR DRVNAME ": failed to unlock WDT change reg\n");
+ pr_err("failed to unlock WDT change reg\n");
}
static void ar7_wdt_disable(u32 value)
@@ -135,7 +136,7 @@ static void ar7_wdt_disable(u32 value)
}
}
}
- printk(KERN_ERR DRVNAME ": failed to unlock WDT disable reg\n");
+ pr_err("failed to unlock WDT disable reg\n");
}
static void ar7_wdt_update_margin(int new_margin)
@@ -151,21 +152,20 @@ static void ar7_wdt_update_margin(int new_margin)
change = 0xffff;
ar7_wdt_change(change);
margin = change * prescale_value / vbus_rate;
- printk(KERN_INFO DRVNAME
- ": timer margin %d seconds (prescale %d, change %d, freq %d)\n",
- margin, prescale_value, change, vbus_rate);
+ pr_info("timer margin %d seconds (prescale %d, change %d, freq %d)\n",
+ margin, prescale_value, change, vbus_rate);
}
static void ar7_wdt_enable_wdt(void)
{
- printk(KERN_DEBUG DRVNAME ": enabling watchdog timer\n");
+ pr_debug("enabling watchdog timer\n");
ar7_wdt_disable(1);
ar7_wdt_kick(1);
}
static void ar7_wdt_disable_wdt(void)
{
- printk(KERN_DEBUG DRVNAME ": disabling watchdog timer\n");
+ pr_debug("disabling watchdog timer\n");
ar7_wdt_disable(0);
}
@@ -183,9 +183,7 @@ static int ar7_wdt_open(struct inode *inode, struct file *file)
static int ar7_wdt_release(struct inode *inode, struct file *file)
{
if (!expect_close)
- printk(KERN_WARNING DRVNAME
- ": watchdog device closed unexpectedly,"
- "will not disable the watchdog timer\n");
+ pr_warn("watchdog device closed unexpectedly, will not disable the watchdog timer\n");
else if (!nowayout)
ar7_wdt_disable_wdt();
clear_bit(0, &wdt_is_open);
@@ -283,28 +281,28 @@ static int __devinit ar7_wdt_probe(struct platform_device *pdev)
ar7_regs_wdt =
platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
if (!ar7_regs_wdt) {
- printk(KERN_ERR DRVNAME ": could not get registers resource\n");
+ pr_err("could not get registers resource\n");
rc = -ENODEV;
goto out;
}
if (!request_mem_region(ar7_regs_wdt->start,
resource_size(ar7_regs_wdt), LONGNAME)) {
- printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n");
+ pr_warn("watchdog I/O region busy\n");
rc = -EBUSY;
goto out;
}
ar7_wdt = ioremap(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
if (!ar7_wdt) {
- printk(KERN_ERR DRVNAME ": could not ioremap registers\n");
+ pr_err("could not ioremap registers\n");
rc = -ENXIO;
goto out_mem_region;
}
vbus_clk = clk_get(NULL, "vbus");
if (IS_ERR(vbus_clk)) {
- printk(KERN_ERR DRVNAME ": could not get vbus clock\n");
+ pr_err("could not get vbus clock\n");
rc = PTR_ERR(vbus_clk);
goto out_mem_region;
}
@@ -315,7 +313,7 @@ static int __devinit ar7_wdt_probe(struct platform_device *pdev)
rc = misc_register(&ar7_wdt_miscdev);
if (rc) {
- printk(KERN_ERR DRVNAME ": unable to register misc device\n");
+ pr_err("unable to register misc device\n");
goto out_alloc;
}
goto out;
diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c
index 4ca5d40..2896430 100644
--- a/drivers/watchdog/at32ap700x_wdt.c
+++ b/drivers/watchdog/at32ap700x_wdt.c
@@ -45,8 +45,8 @@ MODULE_PARM_DESC(timeout,
"Timeout value. Limited to be 1 or 2 seconds. (default="
__MODULE_STRING(TIMEOUT_DEFAULT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c
index 7ceefd2..7ef99a1 100644
--- a/drivers/watchdog/at91rm9200_wdt.c
+++ b/drivers/watchdog/at91rm9200_wdt.c
@@ -9,6 +9,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -28,14 +30,14 @@
#define WDT_MAX_TIME 256 /* seconds */
static int wdt_time = WDT_DEFAULT_TIME;
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(wdt_time, int, 0);
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
__MODULE_STRING(WDT_DEFAULT_TIME) ")");
#ifdef CONFIG_WATCHDOG_NOWAYOUT
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -209,8 +211,8 @@ static int __devinit at91wdt_probe(struct platform_device *pdev)
if (res)
return res;
- printk(KERN_INFO "AT91 Watchdog Timer enabled (%d seconds%s)\n",
- wdt_time, nowayout ? ", nowayout" : "");
+ pr_info("AT91 Watchdog Timer enabled (%d seconds%s)\n",
+ wdt_time, nowayout ? ", nowayout" : "");
return 0;
}
@@ -268,8 +270,8 @@ static int __init at91_wdt_init(void)
if not reset to the default */
if (at91_wdt_settimeout(wdt_time)) {
at91_wdt_settimeout(WDT_DEFAULT_TIME);
- pr_info("at91_wdt: wdt_time value must be 1 <= wdt_time <= 256"
- ", using %d\n", wdt_time);
+ pr_info("wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
+ wdt_time);
}
return platform_driver_register(&at91wdt_driver);
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
index 0056256..05e1be8 100644
--- a/drivers/watchdog/at91sam9_wdt.c
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -15,6 +15,8 @@
* bootloader doesn't write to this register.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
@@ -60,8 +62,8 @@ module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -96,7 +98,7 @@ static void at91_ping(unsigned long data)
at91_wdt_reset();
mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
} else
- printk(KERN_CRIT DRV_NAME": I will reset your machine !\n");
+ pr_crit("I will reset your machine !\n");
}
/*
@@ -140,7 +142,7 @@ static int at91_wdt_settimeout(unsigned int timeout)
/* Check if disabled */
mr = wdt_read(AT91_WDT_MR);
if (mr & AT91_WDT_WDDIS) {
- printk(KERN_ERR DRV_NAME": sorry, watchdog is disabled\n");
+ pr_err("sorry, watchdog is disabled\n");
return -EIO;
}
@@ -283,7 +285,7 @@ static int __init at91wdt_probe(struct platform_device *pdev)
setup_timer(&at91wdt_private.timer, at91_ping, 0);
mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
- printk(KERN_INFO DRV_NAME " enabled (heartbeat=%d sec, nowayout=%d)\n",
+ pr_info("enabled (heartbeat=%d sec, nowayout=%d)\n",
heartbeat, nowayout);
return 0;
diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c
index 9db8083..1f9371f 100644
--- a/drivers/watchdog/ath79_wdt.c
+++ b/drivers/watchdog/ath79_wdt.c
@@ -17,6 +17,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -45,8 +47,8 @@
#define WDOG_CTRL_ACTION_NMI 2 /* NMI */
#define WDOG_CTRL_ACTION_FCR 3 /* full chip reset */
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -114,8 +116,7 @@ static int ath79_wdt_release(struct inode *inode, struct file *file)
if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags))
ath79_wdt_disable();
else {
- pr_crit(DRIVER_NAME ": device closed unexpectedly, "
- "watchdog timer will not stop!\n");
+ pr_crit("device closed unexpectedly, watchdog timer will not stop!\n");
ath79_wdt_keepalive();
}
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
index 5c5f4b1..bc0e91e 100644
--- a/drivers/watchdog/bcm47xx_wdt.c
+++ b/drivers/watchdog/bcm47xx_wdt.c
@@ -10,6 +10,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -33,14 +35,14 @@
#define WDT_MAX_TIME 255 /* seconds */
static int wdt_time = WDT_DEFAULT_TIME;
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(wdt_time, int, 0);
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
__MODULE_STRING(WDT_DEFAULT_TIME) ")");
#ifdef CONFIG_WATCHDOG_NOWAYOUT
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -91,7 +93,7 @@ static void bcm47xx_timer_tick(unsigned long unused)
bcm47xx_wdt_hw_start();
mod_timer(&wdt_timer, jiffies + HZ);
} else {
- printk(KERN_CRIT DRV_NAME "Watchdog will fire soon!!!\n");
+ pr_crit("Watchdog will fire soon!!!\n");
}
}
@@ -140,8 +142,7 @@ static int bcm47xx_wdt_release(struct inode *inode, struct file *file)
if (expect_release == 42) {
bcm47xx_wdt_stop();
} else {
- printk(KERN_CRIT DRV_NAME
- ": Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
bcm47xx_wdt_start();
}
@@ -270,8 +271,7 @@ static int __init bcm47xx_wdt_init(void)
if (bcm47xx_wdt_settimeout(wdt_time)) {
bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME);
- printk(KERN_INFO DRV_NAME ": "
- "wdt_time value must be 0 < wdt_time < %d, using %d\n",
+ pr_info("wdt_time value must be 0 < wdt_time < %d, using %d\n",
(WDT_MAX_TIME + 1), wdt_time);
}
@@ -285,8 +285,8 @@ static int __init bcm47xx_wdt_init(void)
return ret;
}
- printk(KERN_INFO "BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
- wdt_time, nowayout ? ", nowayout" : "");
+ pr_info("BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
+ wdt_time, nowayout ? ", nowayout" : "");
return 0;
}
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 8dc7de6..8379dc3 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -10,6 +10,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -50,8 +52,8 @@ static struct {
static int expect_close;
static int wdt_time = WDT_DEFAULT_TIME;
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -82,7 +84,7 @@ static void bcm63xx_timer_tick(unsigned long unused)
bcm63xx_wdt_hw_start();
mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
} else
- printk(KERN_CRIT PFX ": watchdog will restart system\n");
+ pr_crit("watchdog will restart system\n");
}
static void bcm63xx_wdt_pet(void)
@@ -126,8 +128,7 @@ static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
if (expect_close == 42)
bcm63xx_wdt_pause();
else {
- printk(KERN_CRIT PFX
- ": Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
bcm63xx_wdt_start();
}
clear_bit(0, &bcm63xx_wdt_device.inuse);
diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c
index b9fa9b7..38bc383 100644
--- a/drivers/watchdog/bfin_wdt.c
+++ b/drivers/watchdog/bfin_wdt.c
@@ -11,6 +11,8 @@
* Licensed under the GPL-2 or later.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -28,15 +30,8 @@
#define stamp(fmt, args...) \
pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args)
#define stampit() stamp("here i am")
-#define pr_devinit(fmt, args...) \
- ({ static const __devinitconst char __fmt[] = fmt; \
- printk(__fmt, ## args); })
-#define pr_init(fmt, args...) \
- ({ static const __initconst char __fmt[] = fmt; \
- printk(__fmt, ## args); })
#define WATCHDOG_NAME "bfin-wdt"
-#define PFX WATCHDOG_NAME ": "
/* The BF561 has two watchdogs (one per core), but since Linux
* only runs on core A, we'll just work with that one.
@@ -54,7 +49,7 @@
#define WATCHDOG_TIMEOUT 20
static unsigned int timeout = WATCHDOG_TIMEOUT;
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static const struct watchdog_info bfin_wdt_info;
static unsigned long open_check;
static char expect_close;
@@ -126,7 +121,7 @@ static int bfin_wdt_set_timeout(unsigned long t)
stamp("maxtimeout=%us newtimeout=%lus (cnt=%#x)", max_t, t, cnt);
if (t > max_t) {
- printk(KERN_WARNING PFX "timeout value is too large\n");
+ pr_warn("timeout value is too large\n");
return -EINVAL;
}
@@ -182,8 +177,7 @@ static int bfin_wdt_release(struct inode *inode, struct file *file)
if (expect_close == 42)
bfin_wdt_stop();
else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
bfin_wdt_keepalive();
}
expect_close = 0;
@@ -368,14 +362,13 @@ static int __devinit bfin_wdt_probe(struct platform_device *pdev)
ret = misc_register(&bfin_wdt_miscdev);
if (ret) {
- pr_devinit(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
return ret;
}
- pr_devinit(KERN_INFO PFX "initialized: timeout=%d sec (nowayout=%d)\n",
- timeout, nowayout);
+ pr_info("initialized: timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
return 0;
}
@@ -439,14 +432,14 @@ static int __init bfin_wdt_init(void)
*/
ret = platform_driver_register(&bfin_wdt_driver);
if (ret) {
- pr_init(KERN_ERR PFX "unable to register driver\n");
+ pr_err("unable to register driver\n");
return ret;
}
bfin_wdt_device = platform_device_register_simple(WATCHDOG_NAME,
-1, NULL, 0);
if (IS_ERR(bfin_wdt_device)) {
- pr_init(KERN_ERR PFX "unable to register device\n");
+ pr_err("unable to register device\n");
platform_driver_unregister(&bfin_wdt_driver);
return PTR_ERR(bfin_wdt_device);
}
@@ -479,7 +472,7 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c
index 7c0fdfc..ce0ab44 100644
--- a/drivers/watchdog/booke_wdt.c
+++ b/drivers/watchdog/booke_wdt.c
@@ -12,6 +12,8 @@
* option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/smp.h>
@@ -21,7 +23,6 @@
#include <linux/uaccess.h>
#include <asm/reg_booke.h>
-#include <asm/system.h>
#include <asm/time.h>
#include <asm/div64.h>
@@ -225,8 +226,8 @@ static int booke_wdt_open(struct inode *inode, struct file *file)
if (booke_wdt_enabled == 0) {
booke_wdt_enabled = 1;
on_each_cpu(__booke_wdt_enable, NULL, 0);
- pr_debug("booke_wdt: watchdog enabled (timeout = %llu sec)\n",
- period_to_sec(booke_wdt_period));
+ pr_debug("watchdog enabled (timeout = %llu sec)\n",
+ period_to_sec(booke_wdt_period));
}
spin_unlock(&booke_wdt_lock);
@@ -243,7 +244,7 @@ static int booke_wdt_release(struct inode *inode, struct file *file)
*/
on_each_cpu(__booke_wdt_disable, NULL, 0);
booke_wdt_enabled = 0;
- pr_debug("booke_wdt: watchdog disabled\n");
+ pr_debug("watchdog disabled\n");
#endif
clear_bit(0, &wdt_is_active);
@@ -275,19 +276,19 @@ static int __init booke_wdt_init(void)
{
int ret = 0;
- pr_info("booke_wdt: powerpc book-e watchdog driver loaded\n");
+ pr_info("powerpc book-e watchdog driver loaded\n");
ident.firmware_version = cur_cpu_spec->pvr_value;
ret = misc_register(&booke_wdt_miscdev);
if (ret) {
- pr_err("booke_wdt: cannot register device (minor=%u, ret=%i)\n",
+ pr_err("cannot register device (minor=%u, ret=%i)\n",
WATCHDOG_MINOR, ret);
return ret;
}
spin_lock(&booke_wdt_lock);
if (booke_wdt_enabled == 1) {
- pr_info("booke_wdt: watchdog enabled (timeout = %llu sec)\n",
+ pr_info("watchdog enabled (timeout = %llu sec)\n",
period_to_sec(booke_wdt_period));
on_each_cpu(__booke_wdt_enable, NULL, 0);
}
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
index 5b89f7d..6876430 100644
--- a/drivers/watchdog/coh901327_wdt.c
+++ b/drivers/watchdog/coh901327_wdt.c
@@ -8,17 +8,15 @@
*/
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/bitops.h>
-#include <linux/uaccess.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/err.h>
#define DRV_NAME "WDOG COH 901 327"
@@ -69,13 +67,11 @@
#define U300_WDOG_IFR_WILL_BARK_IRQ_FORCE_ENABLE 0x0001U
/* Default timeout in seconds = 1 minute */
-static int margin = 60;
+static unsigned int margin = 60;
static resource_size_t phybase;
static resource_size_t physize;
static int irq;
static void __iomem *virtbase;
-static unsigned long coh901327_users;
-static unsigned long boot_status;
static struct device *parent;
/*
@@ -155,34 +151,35 @@ static void coh901327_disable(void)
__func__, val);
}
-static void coh901327_start(void)
+static int coh901327_start(struct watchdog_device *wdt_dev)
{
- coh901327_enable(margin * 100);
+ coh901327_enable(wdt_dev->timeout * 100);
+ return 0;
+}
+
+static int coh901327_stop(struct watchdog_device *wdt_dev)
+{
+ coh901327_disable();
+ return 0;
}
-static void coh901327_keepalive(void)
+static int coh901327_ping(struct watchdog_device *wdd)
{
clk_enable(clk);
/* Feed the watchdog */
writew(U300_WDOG_FR_FEED_RESTART_TIMER,
virtbase + U300_WDOG_FR);
clk_disable(clk);
+ return 0;
}
-static int coh901327_settimeout(int time)
+static int coh901327_settimeout(struct watchdog_device *wdt_dev,
+ unsigned int time)
{
- /*
- * Max margin is 327 since the 10ms
- * timeout register is max
- * 0x7FFF = 327670ms ~= 327s.
- */
- if (time <= 0 || time > 327)
- return -EINVAL;
-
- margin = time;
+ wdt_dev->timeout = time;
clk_enable(clk);
/* Set new timeout value */
- writew(margin * 100, virtbase + U300_WDOG_TR);
+ writew(time * 100, virtbase + U300_WDOG_TR);
/* Feed the dog */
writew(U300_WDOG_FR_FEED_RESTART_TIMER,
virtbase + U300_WDOG_FR);
@@ -190,6 +187,23 @@ static int coh901327_settimeout(int time)
return 0;
}
+static unsigned int coh901327_gettimeleft(struct watchdog_device *wdt_dev)
+{
+ u16 val;
+
+ clk_enable(clk);
+ /* Read repeatedly until the value is stable! */
+ val = readw(virtbase + U300_WDOG_CR);
+ while (val & U300_WDOG_CR_VALID_IND)
+ val = readw(virtbase + U300_WDOG_CR);
+ val &= U300_WDOG_CR_COUNT_VALUE_MASK;
+ clk_disable(clk);
+ if (val != 0)
+ val /= 100;
+
+ return val;
+}
+
/*
* This interrupt occurs 10 ms before the watchdog WILL bark.
*/
@@ -218,130 +232,35 @@ static irqreturn_t coh901327_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
-/*
- * Allow only one user (daemon) to open the watchdog
- */
-static int coh901327_open(struct inode *inode, struct file *file)
-{
- if (test_and_set_bit(1, &coh901327_users))
- return -EBUSY;
- coh901327_start();
- return nonseekable_open(inode, file);
-}
-
-static int coh901327_release(struct inode *inode, struct file *file)
-{
- clear_bit(1, &coh901327_users);
- coh901327_disable();
- return 0;
-}
-
-static ssize_t coh901327_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
-{
- if (len)
- coh901327_keepalive();
- return len;
-}
-
-static long coh901327_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int ret = -ENOTTY;
- u16 val;
- int time;
- int new_options;
- union {
- struct watchdog_info __user *ident;
- int __user *i;
- } uarg;
- static const struct watchdog_info ident = {
- .options = WDIOF_CARDRESET |
- WDIOF_SETTIMEOUT |
- WDIOF_KEEPALIVEPING,
- .identity = "COH 901 327 Watchdog",
- .firmware_version = 1,
- };
- uarg.i = (int __user *)arg;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = copy_to_user(uarg.ident, &ident,
- sizeof(ident)) ? -EFAULT : 0;
- break;
-
- case WDIOC_GETSTATUS:
- ret = put_user(0, uarg.i);
- break;
-
- case WDIOC_GETBOOTSTATUS:
- ret = put_user(boot_status, uarg.i);
- break;
-
- case WDIOC_SETOPTIONS:
- ret = get_user(new_options, uarg.i);
- if (ret)
- break;
- if (new_options & WDIOS_DISABLECARD)
- coh901327_disable();
- if (new_options & WDIOS_ENABLECARD)
- coh901327_start();
- ret = 0;
- break;
-
- case WDIOC_KEEPALIVE:
- coh901327_keepalive();
- ret = 0;
- break;
-
- case WDIOC_SETTIMEOUT:
- ret = get_user(time, uarg.i);
- if (ret)
- break;
-
- ret = coh901327_settimeout(time);
- if (ret)
- break;
- /* Then fall through to return set value */
-
- case WDIOC_GETTIMEOUT:
- ret = put_user(margin, uarg.i);
- break;
-
- case WDIOC_GETTIMELEFT:
- clk_enable(clk);
- /* Read repeatedly until the value is stable! */
- val = readw(virtbase + U300_WDOG_CR);
- while (val & U300_WDOG_CR_VALID_IND)
- val = readw(virtbase + U300_WDOG_CR);
- val &= U300_WDOG_CR_COUNT_VALUE_MASK;
- clk_disable(clk);
- if (val != 0)
- val /= 100;
- ret = put_user(val, uarg.i);
- break;
- }
- return ret;
-}
+static const struct watchdog_info coh901327_ident = {
+ .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = DRV_NAME,
+};
-static const struct file_operations coh901327_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = coh901327_write,
- .unlocked_ioctl = coh901327_ioctl,
- .open = coh901327_open,
- .release = coh901327_release,
+static struct watchdog_ops coh901327_ops = {
+ .owner = THIS_MODULE,
+ .start = coh901327_start,
+ .stop = coh901327_stop,
+ .ping = coh901327_ping,
+ .set_timeout = coh901327_settimeout,
+ .get_timeleft = coh901327_gettimeleft,
};
-static struct miscdevice coh901327_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &coh901327_fops,
+static struct watchdog_device coh901327_wdt = {
+ .info = &coh901327_ident,
+ .ops = &coh901327_ops,
+ /*
+ * Max timeout is 327 since the 10ms
+ * timeout register is max
+ * 0x7FFF = 327670ms ~= 327s.
+ */
+ .min_timeout = 0,
+ .max_timeout = 327,
};
static int __exit coh901327_remove(struct platform_device *pdev)
{
- misc_deregister(&coh901327_miscdev);
+ watchdog_unregister_device(&coh901327_wdt);
coh901327_disable();
free_irq(irq, pdev);
clk_put(clk);
@@ -350,7 +269,6 @@ static int __exit coh901327_remove(struct platform_device *pdev)
return 0;
}
-
static int __init coh901327_probe(struct platform_device *pdev)
{
int ret;
@@ -393,7 +311,7 @@ static int __init coh901327_probe(struct platform_device *pdev)
case U300_WDOG_SR_STATUS_TIMED_OUT:
dev_info(&pdev->dev,
"watchdog timed out since last chip reset!\n");
- boot_status = WDIOF_CARDRESET;
+ coh901327_wdt.bootstatus |= WDIOF_CARDRESET;
/* Status will be cleared below */
break;
case U300_WDOG_SR_STATUS_NORMAL:
@@ -435,7 +353,11 @@ static int __init coh901327_probe(struct platform_device *pdev)
clk_disable(clk);
- ret = misc_register(&coh901327_miscdev);
+ if (margin < 1 || margin > 327)
+ margin = 60;
+ coh901327_wdt.timeout = margin;
+
+ ret = watchdog_register_device(&coh901327_wdt);
if (ret == 0)
dev_info(&pdev->dev,
"initialized. timer margin=%d sec\n", margin);
@@ -543,8 +465,8 @@ module_exit(coh901327_exit);
MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
MODULE_DESCRIPTION("COH 901 327 Watchdog");
-module_param(margin, int, 0);
+module_param(margin, uint, 0);
MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:coh901327-watchdog");
diff --git a/drivers/watchdog/cpu5wdt.c b/drivers/watchdog/cpu5wdt.c
index 251c863..7e88839 100644
--- a/drivers/watchdog/cpu5wdt.c
+++ b/drivers/watchdog/cpu5wdt.c
@@ -19,6 +19,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -71,7 +73,7 @@ static struct {
static void cpu5wdt_trigger(unsigned long unused)
{
if (verbose > 2)
- printk(KERN_DEBUG PFX "trigger at %i ticks\n", ticks);
+ pr_debug("trigger at %i ticks\n", ticks);
if (cpu5wdt_device.running)
ticks--;
@@ -96,7 +98,7 @@ static void cpu5wdt_reset(void)
ticks = cpu5wdt_device.default_ticks;
if (verbose)
- printk(KERN_DEBUG PFX "reset (%i ticks)\n", (int) ticks);
+ pr_debug("reset (%i ticks)\n", (int) ticks);
}
@@ -129,7 +131,7 @@ static int cpu5wdt_stop(void)
ticks = cpu5wdt_device.default_ticks;
spin_unlock_irqrestore(&cpu5wdt_lock, flags);
if (verbose)
- printk(KERN_CRIT PFX "stop not possible\n");
+ pr_crit("stop not possible\n");
return -EIO;
}
@@ -219,8 +221,7 @@ static int __devinit cpu5wdt_init(void)
int err;
if (verbose)
- printk(KERN_DEBUG PFX
- "port=0x%x, verbose=%i\n", port, verbose);
+ pr_debug("port=0x%x, verbose=%i\n", port, verbose);
init_completion(&cpu5wdt_device.stop);
cpu5wdt_device.queue = 0;
@@ -228,7 +229,7 @@ static int __devinit cpu5wdt_init(void)
cpu5wdt_device.default_ticks = ticks;
if (!request_region(port, CPU5WDT_EXTENT, PFX)) {
- printk(KERN_ERR PFX "request_region failed\n");
+ pr_err("request_region failed\n");
err = -EBUSY;
goto no_port;
}
@@ -237,16 +238,16 @@ static int __devinit cpu5wdt_init(void)
val = inb(port + CPU5WDT_STATUS_REG);
val = (val >> 2) & 1;
if (!val)
- printk(KERN_INFO PFX "sorry, was my fault\n");
+ pr_info("sorry, was my fault\n");
err = misc_register(&cpu5wdt_misc);
if (err < 0) {
- printk(KERN_ERR PFX "misc_register failed\n");
+ pr_err("misc_register failed\n");
goto no_misc;
}
- printk(KERN_INFO PFX "init success\n");
+ pr_info("init success\n");
return 0;
no_misc:
diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c
index 1b793df..95b1b95 100644
--- a/drivers/watchdog/cpwd.c
+++ b/drivers/watchdog/cpwd.c
@@ -14,6 +14,8 @@
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
@@ -35,7 +37,6 @@
#include <asm/watchdog.h>
#define DRIVER_NAME "cpwd"
-#define PFX DRIVER_NAME ": "
#define WD_OBPNAME "watchdog"
#define WD_BADMODEL "SUNW,501-5336"
@@ -385,8 +386,7 @@ static int cpwd_open(struct inode *inode, struct file *f)
if (!p->initialized) {
if (request_irq(p->irq, &cpwd_interrupt,
IRQF_SHARED, DRIVER_NAME, p)) {
- printk(KERN_ERR PFX "Cannot register IRQ %d\n",
- p->irq);
+ pr_err("Cannot register IRQ %d\n", p->irq);
mutex_unlock(&cpwd_mutex);
return -EBUSY;
}
@@ -542,7 +542,7 @@ static int __devinit cpwd_probe(struct platform_device *op)
p = kzalloc(sizeof(*p), GFP_KERNEL);
err = -ENOMEM;
if (!p) {
- printk(KERN_ERR PFX "Unable to allocate struct cpwd.\n");
+ pr_err("Unable to allocate struct cpwd\n");
goto out;
}
@@ -553,14 +553,14 @@ static int __devinit cpwd_probe(struct platform_device *op)
p->regs = of_ioremap(&op->resource[0], 0,
4 * WD_TIMER_REGSZ, DRIVER_NAME);
if (!p->regs) {
- printk(KERN_ERR PFX "Unable to map registers.\n");
+ pr_err("Unable to map registers\n");
goto out_free;
}
options = of_find_node_by_path("/options");
err = -ENODEV;
if (!options) {
- printk(KERN_ERR PFX "Unable to find /options node.\n");
+ pr_err("Unable to find /options node\n");
goto out_iounmap;
}
@@ -605,8 +605,8 @@ static int __devinit cpwd_probe(struct platform_device *op)
err = misc_register(&p->devs[i].misc);
if (err) {
- printk(KERN_ERR "Could not register misc device for "
- "dev %d\n", i);
+ pr_err("Could not register misc device for dev %d\n",
+ i);
goto out_unregister;
}
}
@@ -617,8 +617,8 @@ static int __devinit cpwd_probe(struct platform_device *op)
cpwd_timer.data = (unsigned long) p;
cpwd_timer.expires = WD_BTIMEOUT;
- printk(KERN_INFO PFX "PLD defect workaround enabled for "
- "model " WD_BADMODEL ".\n");
+ pr_info("PLD defect workaround enabled for model %s\n",
+ WD_BADMODEL);
}
dev_set_drvdata(&op->dev, p);
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c
index 63d7b58..06de121 100644
--- a/drivers/watchdog/dw_wdt.c
+++ b/drivers/watchdog/dw_wdt.c
@@ -16,7 +16,8 @@
* If we receive an expected close for the watchdog then we keep the timer
* running, otherwise the timer is stopped and the watchdog will expire.
*/
-#define pr_fmt(fmt) "dw_wdt: " fmt
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitops.h>
#include <linux/clk.h>
@@ -45,8 +46,8 @@
/* The maximum TOP (timeout period) value that can be set in the watchdog. */
#define DW_WDT_MAX_TOP 15
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c
index 09cd888..7705003 100644
--- a/drivers/watchdog/ep93xx_wdt.c
+++ b/drivers/watchdog/ep93xx_wdt.c
@@ -8,6 +8,9 @@
* Authors: Ray Lehtiniemi <rayl@mail.com>,
* Alessandro Zummo <a.zummo@towertech.it>
*
+ * Copyright (c) 2012 H Hartley Sweeten <hsweeten@visionengravers.com>
+ * Convert to a platform device and use the watchdog framework API
+ *
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
@@ -25,187 +28,90 @@
#include <linux/platform_device.h>
#include <linux/module.h>
-#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/timer.h>
-#include <linux/uaccess.h>
#include <linux/io.h>
-#define WDT_VERSION "0.3"
-#define PFX "ep93xx_wdt: "
+#define WDT_VERSION "0.4"
/* default timeout (secs) */
#define WDT_TIMEOUT 30
-static int nowayout = WATCHDOG_NOWAYOUT;
-static int timeout = WDT_TIMEOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+static unsigned int timeout = WDT_TIMEOUT;
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. (1<=timeout<=3600, default="
+ __MODULE_STRING(WDT_TIMEOUT) ")");
static void __iomem *mmio_base;
static struct timer_list timer;
static unsigned long next_heartbeat;
-static unsigned long wdt_status;
-static unsigned long boot_status;
-
-#define WDT_IN_USE 0
-#define WDT_OK_TO_CLOSE 1
#define EP93XX_WATCHDOG 0x00
#define EP93XX_WDSTATUS 0x04
-/* reset the wdt every ~200ms */
+/* reset the wdt every ~200ms - the heartbeat of the device is 0.250 seconds*/
#define WDT_INTERVAL (HZ/5)
-static void wdt_enable(void)
+static void ep93xx_wdt_timer_ping(unsigned long data)
{
- writel(0xaaaa, mmio_base + EP93XX_WATCHDOG);
-}
-
-static void wdt_disable(void)
-{
- writel(0xaa55, mmio_base + EP93XX_WATCHDOG);
-}
+ if (time_before(jiffies, next_heartbeat))
+ writel(0x5555, mmio_base + EP93XX_WATCHDOG);
-static inline void wdt_ping(void)
-{
- writel(0x5555, mmio_base + EP93XX_WATCHDOG);
+ /* Re-set the timer interval */
+ mod_timer(&timer, jiffies + WDT_INTERVAL);
}
-static void wdt_startup(void)
+static int ep93xx_wdt_start(struct watchdog_device *wdd)
{
next_heartbeat = jiffies + (timeout * HZ);
- wdt_enable();
+ writel(0xaaaa, mmio_base + EP93XX_WATCHDOG);
mod_timer(&timer, jiffies + WDT_INTERVAL);
+
+ return 0;
}
-static void wdt_shutdown(void)
+static int ep93xx_wdt_stop(struct watchdog_device *wdd)
{
del_timer_sync(&timer);
- wdt_disable();
+ writel(0xaa55, mmio_base + EP93XX_WATCHDOG);
+
+ return 0;
}
-static void wdt_keepalive(void)
+static int ep93xx_wdt_keepalive(struct watchdog_device *wdd)
{
/* user land ping */
next_heartbeat = jiffies + (timeout * HZ);
-}
-
-static int ep93xx_wdt_open(struct inode *inode, struct file *file)
-{
- if (test_and_set_bit(WDT_IN_USE, &wdt_status))
- return -EBUSY;
-
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
- wdt_startup();
-
- return nonseekable_open(inode, file);
-}
-
-static ssize_t
-ep93xx_wdt_write(struct file *file, const char __user *data, size_t len,
- loff_t *ppos)
-{
- if (len) {
- if (!nowayout) {
- size_t i;
-
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
- for (i = 0; i != len; i++) {
- char c;
-
- if (get_user(c, data + i))
- return -EFAULT;
-
- if (c == 'V')
- set_bit(WDT_OK_TO_CLOSE, &wdt_status);
- else
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
- }
- }
- wdt_keepalive();
- }
- return len;
+ return 0;
}
-static const struct watchdog_info ident = {
- .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE,
- .identity = "EP93xx Watchdog",
+static const struct watchdog_info ep93xx_wdt_ident = {
+ .options = WDIOF_CARDRESET |
+ WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING,
+ .identity = "EP93xx Watchdog",
};
-static long ep93xx_wdt_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int ret = -ENOTTY;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
- sizeof(ident)) ? -EFAULT : 0;
- break;
-
- case WDIOC_GETSTATUS:
- ret = put_user(0, (int __user *)arg);
- break;
-
- case WDIOC_GETBOOTSTATUS:
- ret = put_user(boot_status, (int __user *)arg);
- break;
-
- case WDIOC_KEEPALIVE:
- wdt_keepalive();
- ret = 0;
- break;
-
- case WDIOC_GETTIMEOUT:
- /* actually, it is 0.250 seconds.... */
- ret = put_user(1, (int __user *)arg);
- break;
- }
- return ret;
-}
-
-static int ep93xx_wdt_release(struct inode *inode, struct file *file)
-{
- if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
- wdt_shutdown();
- else
- printk(KERN_CRIT PFX
- "Device closed unexpectedly - timer will not stop\n");
-
- clear_bit(WDT_IN_USE, &wdt_status);
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
- return 0;
-}
-
-static const struct file_operations ep93xx_wdt_fops = {
+static struct watchdog_ops ep93xx_wdt_ops = {
.owner = THIS_MODULE,
- .write = ep93xx_wdt_write,
- .unlocked_ioctl = ep93xx_wdt_ioctl,
- .open = ep93xx_wdt_open,
- .release = ep93xx_wdt_release,
- .llseek = no_llseek,
+ .start = ep93xx_wdt_start,
+ .stop = ep93xx_wdt_stop,
+ .ping = ep93xx_wdt_keepalive,
};
-static struct miscdevice ep93xx_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &ep93xx_wdt_fops,
+static struct watchdog_device ep93xx_wdt_wdd = {
+ .info = &ep93xx_wdt_ident,
+ .ops = &ep93xx_wdt_ops,
};
-static void ep93xx_timer_ping(unsigned long data)
-{
- if (time_before(jiffies, next_heartbeat))
- wdt_ping();
-
- /* Re-set the timer interval */
- mod_timer(&timer, jiffies + WDT_INTERVAL);
-}
-
static int __devinit ep93xx_wdt_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -224,30 +130,35 @@ static int __devinit ep93xx_wdt_probe(struct platform_device *pdev)
if (!mmio_base)
return -ENXIO;
- err = misc_register(&ep93xx_wdt_miscdev);
-
- val = readl(mmio_base + EP93XX_WATCHDOG);
- boot_status = val & 0x01 ? 1 : 0;
-
- printk(KERN_INFO PFX "EP93XX watchdog, driver version "
- WDT_VERSION "%s\n",
- (val & 0x08) ? " (nCS1 disable detected)" : "");
-
if (timeout < 1 || timeout > 3600) {
timeout = WDT_TIMEOUT;
- printk(KERN_INFO PFX
+ dev_warn(&pdev->dev,
"timeout value must be 1<=x<=3600, using %d\n",
timeout);
}
- setup_timer(&timer, ep93xx_timer_ping, 1);
- return err;
+ val = readl(mmio_base + EP93XX_WATCHDOG);
+ ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0;
+ ep93xx_wdt_wdd.timeout = timeout;
+
+ watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout);
+
+ setup_timer(&timer, ep93xx_wdt_timer_ping, 1);
+
+ err = watchdog_register_device(&ep93xx_wdt_wdd);
+ if (err)
+ return err;
+
+ dev_info(&pdev->dev,
+ "EP93XX watchdog, driver version " WDT_VERSION "%s\n",
+ (val & 0x08) ? " (nCS1 disable detected)" : "");
+
+ return 0;
}
static int __devexit ep93xx_wdt_remove(struct platform_device *pdev)
{
- wdt_shutdown();
- misc_deregister(&ep93xx_wdt_miscdev);
+ watchdog_unregister_device(&ep93xx_wdt_wdd);
return 0;
}
@@ -262,16 +173,9 @@ static struct platform_driver ep93xx_wdt_driver = {
module_platform_driver(ep93xx_wdt_driver);
-module_param(nowayout, int, 0);
-MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
-
-module_param(timeout, int, 0);
-MODULE_PARM_DESC(timeout,
- "Watchdog timeout in seconds. (1<=timeout<=3600, default="
- __MODULE_STRING(WDT_TIMEOUT) ")");
-
MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>,"
- "Alessandro Zummo <a.zummo@towertech.it>");
+ "Alessandro Zummo <a.zummo@towertech.it>,"
+ "H Hartley Sweeten <hsweeten@visionengravers.com>");
MODULE_DESCRIPTION("EP93xx Watchdog");
MODULE_LICENSE("GPL");
MODULE_VERSION(WDT_VERSION);
diff --git a/drivers/watchdog/eurotechwdt.c b/drivers/watchdog/eurotechwdt.c
index 3946c51..cd31b8a 100644
--- a/drivers/watchdog/eurotechwdt.c
+++ b/drivers/watchdog/eurotechwdt.c
@@ -45,6 +45,8 @@
* of the on-board SUPER I/O device SMSC FDC 37B782.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -59,7 +61,6 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
static unsigned long eurwdt_is_open;
static int eurwdt_timeout;
@@ -76,8 +77,8 @@ static char *ev = "int";
#define WDT_TIMEOUT 60 /* 1 minute */
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -144,11 +145,11 @@ static void eurwdt_activate_timer(void)
/* Setting interrupt line */
if (irq == 2 || irq > 15 || irq < 0) {
- printk(KERN_ERR ": invalid irq number\n");
+ pr_err("invalid irq number\n");
irq = 0; /* if invalid we disable interrupt */
}
if (irq == 0)
- printk(KERN_INFO ": interrupt disabled\n");
+ pr_info("interrupt disabled\n");
eurwdt_write_reg(WDT_TIMER_CFG, irq << 4);
@@ -163,12 +164,12 @@ static void eurwdt_activate_timer(void)
static irqreturn_t eurwdt_interrupt(int irq, void *dev_id)
{
- printk(KERN_CRIT "timeout WDT timeout\n");
+ pr_crit("timeout WDT timeout\n");
#ifdef ONLY_TESTING
- printk(KERN_CRIT "Would Reboot.\n");
+ pr_crit("Would Reboot\n");
#else
- printk(KERN_CRIT "Initiating system reboot.\n");
+ pr_crit("Initiating system reboot\n");
emergency_restart();
#endif
return IRQ_HANDLED;
@@ -335,8 +336,7 @@ static int eurwdt_release(struct inode *inode, struct file *file)
if (eur_expect_close == 42)
eurwdt_disable_timer();
else {
- printk(KERN_CRIT
- "eurwdt: Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
eurwdt_ping();
}
clear_bit(0, &eurwdt_is_open);
@@ -429,35 +429,32 @@ static int __init eurwdt_init(void)
ret = request_irq(irq, eurwdt_interrupt, 0, "eurwdt", NULL);
if (ret) {
- printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq);
+ pr_err("IRQ %d is not free\n", irq);
goto out;
}
if (!request_region(io, 2, "eurwdt")) {
- printk(KERN_ERR "eurwdt: IO %X is not free.\n", io);
+ pr_err("IO %X is not free\n", io);
ret = -EBUSY;
goto outirq;
}
ret = register_reboot_notifier(&eurwdt_notifier);
if (ret) {
- printk(KERN_ERR
- "eurwdt: can't register reboot notifier (err=%d)\n", ret);
+ pr_err("can't register reboot notifier (err=%d)\n", ret);
goto outreg;
}
ret = misc_register(&eurwdt_miscdev);
if (ret) {
- printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n",
- WATCHDOG_MINOR);
+ pr_err("can't misc_register on minor=%d\n", WATCHDOG_MINOR);
goto outreboot;
}
eurwdt_unlock_chip();
ret = 0;
- printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)"
- " - timeout event: %s\n",
+ pr_info("Eurotech WDT driver 0.01 at %X (Interrupt %d) - timeout event: %s\n",
io, irq, (!strcmp("int", ev) ? "int" : "reboot"));
out:
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c
index e45ca2b..c65b0a5 100644
--- a/drivers/watchdog/f71808e_wdt.c
+++ b/drivers/watchdog/f71808e_wdt.c
@@ -19,6 +19,8 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/init.h>
@@ -189,8 +191,7 @@ static inline int superio_enter(int base)
{
/* Don't step on other drivers' I/O space by accident */
if (!request_muxed_region(base, 2, DRVNAME)) {
- printk(KERN_ERR DRVNAME ": I/O address 0x%04x already in use\n",
- (int)base);
+ pr_err("I/O address 0x%04x already in use\n", (int)base);
return -EBUSY;
}
@@ -217,7 +218,7 @@ static int watchdog_set_timeout(int timeout)
{
if (timeout <= 0
|| timeout > max_timeout) {
- printk(KERN_ERR DRVNAME ": watchdog timeout out of range\n");
+ pr_err("watchdog timeout out of range\n");
return -EINVAL;
}
@@ -252,7 +253,7 @@ static int watchdog_set_pulse_width(unsigned int pw)
} else if (pw <= 5000) {
watchdog.pulse_val = 3;
} else {
- printk(KERN_ERR DRVNAME ": pulse width out of range\n");
+ pr_err("pulse width out of range\n");
err = -EINVAL;
goto exit_unlock;
}
@@ -309,8 +310,7 @@ static int f71862fg_pin_configure(unsigned short ioaddr)
if (ioaddr)
superio_set_bit(ioaddr, SIO_REG_MFUNCT1, 1);
} else {
- printk(KERN_ERR DRVNAME ": Invalid argument f71862fg_pin=%d\n",
- f71862fg_pin);
+ pr_err("Invalid argument f71862fg_pin=%d\n", f71862fg_pin);
return -EINVAL;
}
return 0;
@@ -487,8 +487,7 @@ static int watchdog_release(struct inode *inode, struct file *file)
if (!watchdog.expect_close) {
watchdog_keepalive();
- printk(KERN_CRIT DRVNAME
- ": Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
} else if (!nowayout) {
watchdog_stop();
}
@@ -672,25 +671,22 @@ static int __init watchdog_init(int sioaddr)
err = misc_register(&watchdog_miscdev);
if (err) {
- printk(KERN_ERR DRVNAME
- ": cannot register miscdev on minor=%d\n",
- watchdog_miscdev.minor);
+ pr_err("cannot register miscdev on minor=%d\n",
+ watchdog_miscdev.minor);
goto exit_reboot;
}
if (start_withtimeout) {
if (start_withtimeout <= 0
|| start_withtimeout > max_timeout) {
- printk(KERN_ERR DRVNAME
- ": starting timeout out of range\n");
+ pr_err("starting timeout out of range\n");
err = -EINVAL;
goto exit_miscdev;
}
err = watchdog_start();
if (err) {
- printk(KERN_ERR DRVNAME
- ": cannot start watchdog timer\n");
+ pr_err("cannot start watchdog timer\n");
goto exit_miscdev;
}
@@ -720,8 +716,7 @@ static int __init watchdog_init(int sioaddr)
if (nowayout)
__module_get(THIS_MODULE);
- printk(KERN_INFO DRVNAME
- ": watchdog started with initial timeout of %u sec\n",
+ pr_info("watchdog started with initial timeout of %u sec\n",
start_withtimeout);
}
@@ -746,7 +741,7 @@ static int __init f71808e_find(int sioaddr)
devid = superio_inw(sioaddr, SIO_REG_MANID);
if (devid != SIO_FINTEK_ID) {
- pr_debug(DRVNAME ": Not a Fintek device\n");
+ pr_debug("Not a Fintek device\n");
err = -ENODEV;
goto exit;
}
@@ -774,13 +769,13 @@ static int __init f71808e_find(int sioaddr)
err = -ENODEV;
goto exit;
default:
- printk(KERN_INFO DRVNAME ": Unrecognized Fintek device: %04x\n",
- (unsigned int)devid);
+ pr_info("Unrecognized Fintek device: %04x\n",
+ (unsigned int)devid);
err = -ENODEV;
goto exit;
}
- printk(KERN_INFO DRVNAME ": Found %s watchdog chip, revision %d\n",
+ pr_info("Found %s watchdog chip, revision %d\n",
f71808e_names[watchdog.type],
(int)superio_inb(sioaddr, SIO_REG_DEVREV));
exit:
@@ -808,8 +803,7 @@ static int __init f71808e_init(void)
static void __exit f71808e_exit(void)
{
if (watchdog_is_running()) {
- printk(KERN_WARNING DRVNAME
- ": Watchdog timer still running, stopping it\n");
+ pr_warn("Watchdog timer still running, stopping it\n");
watchdog_stop();
}
misc_deregister(&watchdog_miscdev);
diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c
index b146082..17f4cae 100644
--- a/drivers/watchdog/gef_wdt.c
+++ b/drivers/watchdog/gef_wdt.c
@@ -24,6 +24,8 @@
* capabilities) a kernel-based watchdog.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/init.h>
@@ -68,8 +70,8 @@ static unsigned int bus_clk;
static char expect_close;
static DEFINE_SPINLOCK(gef_wdt_spinlock);
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -110,7 +112,7 @@ static void gef_wdt_handler_enable(void)
if (gef_wdt_toggle_wdc(GEF_WDC_ENABLED_FALSE,
GEF_WDC_ENABLE_SHIFT)) {
gef_wdt_service();
- printk(KERN_NOTICE "gef_wdt: watchdog activated\n");
+ pr_notice("watchdog activated\n");
}
}
@@ -118,7 +120,7 @@ static void gef_wdt_handler_disable(void)
{
if (gef_wdt_toggle_wdc(GEF_WDC_ENABLED_TRUE,
GEF_WDC_ENABLE_SHIFT))
- printk(KERN_NOTICE "gef_wdt: watchdog deactivated\n");
+ pr_notice("watchdog deactivated\n");
}
static void gef_wdt_set_timeout(unsigned int timeout)
@@ -234,8 +236,7 @@ static int gef_wdt_release(struct inode *inode, struct file *file)
if (expect_close == 42)
gef_wdt_handler_disable();
else {
- printk(KERN_CRIT
- "gef_wdt: unexpected close, not stopping timer!\n");
+ pr_crit("unexpected close, not stopping timer!\n");
gef_wdt_service();
}
expect_close = 0;
@@ -313,7 +314,7 @@ static struct platform_driver gef_wdt_driver = {
static int __init gef_wdt_init(void)
{
- printk(KERN_INFO "GE watchdog driver\n");
+ pr_info("GE watchdog driver\n");
return platform_driver_register(&gef_wdt_driver);
}
diff --git a/drivers/watchdog/geodewdt.c b/drivers/watchdog/geodewdt.c
index 9b49b12..dc563b6 100644
--- a/drivers/watchdog/geodewdt.c
+++ b/drivers/watchdog/geodewdt.c
@@ -9,6 +9,7 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -39,8 +40,8 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. 1<= timeout <=131, default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -100,7 +101,7 @@ static int geodewdt_release(struct inode *inode, struct file *file)
geodewdt_disable();
module_put(THIS_MODULE);
} else {
- printk(KERN_CRIT "Unexpected close - watchdog is not stopping.\n");
+ pr_crit("Unexpected close - watchdog is not stopping\n");
geodewdt_ping();
set_bit(WDT_FLAGS_ORPHAN, &wdt_flags);
@@ -220,7 +221,7 @@ static int __devinit geodewdt_probe(struct platform_device *dev)
wdt_timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
if (!wdt_timer) {
- printk(KERN_ERR "geodewdt: No timers were available\n");
+ pr_err("No timers were available\n");
return -ENODEV;
}
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 3c166d3..cbc7cee 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -13,6 +13,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
@@ -45,7 +47,7 @@
static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */
static unsigned int reload; /* the computed soft_margin */
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static char expect_release;
static unsigned long hpwdt_is_open;
@@ -235,8 +237,7 @@ static int __devinit cru_detect(unsigned long map_entry,
asminline_call(&cmn_regs, bios32_entrypoint);
if (cmn_regs.u1.ral != 0) {
- printk(KERN_WARNING
- "hpwdt: Call succeeded but with an error: 0x%x\n",
+ pr_warn("Call succeeded but with an error: 0x%x\n",
cmn_regs.u1.ral);
} else {
physical_bios_base = cmn_regs.u2.rebx;
@@ -256,14 +257,10 @@ static int __devinit cru_detect(unsigned long map_entry,
}
}
- printk(KERN_DEBUG "hpwdt: CRU Base Address: 0x%lx\n",
- physical_bios_base);
- printk(KERN_DEBUG "hpwdt: CRU Offset Address: 0x%lx\n",
- physical_bios_offset);
- printk(KERN_DEBUG "hpwdt: CRU Length: 0x%lx\n",
- cru_length);
- printk(KERN_DEBUG "hpwdt: CRU Mapped Address: %p\n",
- &cru_rom_addr);
+ pr_debug("CRU Base Address: 0x%lx\n", physical_bios_base);
+ pr_debug("CRU Offset Address: 0x%lx\n", physical_bios_offset);
+ pr_debug("CRU Length: 0x%lx\n", cru_length);
+ pr_debug("CRU Mapped Address: %p\n", &cru_rom_addr);
}
iounmap(bios32_map);
return retval;
@@ -458,16 +455,13 @@ static void hpwdt_ping(void)
static int hpwdt_change_timer(int new_margin)
{
if (new_margin < 1 || new_margin > HPWDT_MAX_TIMER) {
- printk(KERN_WARNING
- "hpwdt: New value passed in is invalid: %d seconds.\n",
+ pr_warn("New value passed in is invalid: %d seconds\n",
new_margin);
return -EINVAL;
}
soft_margin = new_margin;
- printk(KERN_DEBUG
- "hpwdt: New timer passed in is %d seconds.\n",
- new_margin);
+ pr_debug("New timer passed in is %d seconds\n", new_margin);
reload = SECS_TO_TICKS(soft_margin);
return 0;
@@ -535,8 +529,7 @@ static int hpwdt_release(struct inode *inode, struct file *file)
if (expect_release == 42) {
hpwdt_stop();
} else {
- printk(KERN_CRIT
- "hpwdt: Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
hpwdt_ping();
}
@@ -881,7 +874,7 @@ MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
module_param(soft_margin, int, 0);
MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c
index db45091..738032a 100644
--- a/drivers/watchdog/i6300esb.c
+++ b/drivers/watchdog/i6300esb.c
@@ -27,6 +27,8 @@
* Includes, defines, variables, module parameters, ...
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
@@ -44,7 +46,6 @@
#define ESB_VERSION "0.05"
#define ESB_MODULE_NAME "i6300ESB timer"
#define ESB_DRIVER_NAME ESB_MODULE_NAME ", v" ESB_VERSION
-#define PFX ESB_MODULE_NAME ": "
/* PCI configuration registers */
#define ESB_CONFIG_REG 0x60 /* Config register */
@@ -94,8 +95,8 @@ MODULE_PARM_DESC(heartbeat,
"Watchdog heartbeat in seconds. (1<heartbeat<2046, default="
__MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -213,8 +214,7 @@ static int esb_release(struct inode *inode, struct file *file)
if (esb_expect_close == 42)
esb_timer_stop();
else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
esb_timer_keepalive();
}
clear_bit(0, &timer_alive);
@@ -347,19 +347,19 @@ MODULE_DEVICE_TABLE(pci, esb_pci_tbl);
static unsigned char __devinit esb_getdevice(struct pci_dev *pdev)
{
if (pci_enable_device(pdev)) {
- printk(KERN_ERR PFX "failed to enable device\n");
+ pr_err("failed to enable device\n");
goto err_devput;
}
if (pci_request_region(pdev, 0, ESB_MODULE_NAME)) {
- printk(KERN_ERR PFX "failed to request region\n");
+ pr_err("failed to request region\n");
goto err_disable;
}
BASEADDR = pci_ioremap_bar(pdev, 0);
if (BASEADDR == NULL) {
/* Something's wrong here, BASEADDR has to be set */
- printk(KERN_ERR PFX "failed to get BASEADDR\n");
+ pr_err("failed to get BASEADDR\n");
goto err_release;
}
@@ -397,7 +397,7 @@ static void __devinit esb_initdevice(void)
/* Check that the WDT isn't already locked */
pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1);
if (val1 & ESB_WDT_LOCK)
- printk(KERN_WARNING PFX "nowayout already set\n");
+ pr_warn("nowayout already set\n");
/* Set the timer to watchdog mode and disable it for now */
pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00);
@@ -423,11 +423,11 @@ static int __devinit esb_probe(struct pci_dev *pdev,
cards_found++;
if (cards_found == 1)
- printk(KERN_INFO PFX "Intel 6300ESB WatchDog Timer Driver v%s\n",
+ pr_info("Intel 6300ESB WatchDog Timer Driver v%s\n",
ESB_VERSION);
if (cards_found > 1) {
- printk(KERN_ERR PFX "This driver only supports 1 device\n");
+ pr_err("This driver only supports 1 device\n");
return -ENODEV;
}
@@ -439,9 +439,8 @@ static int __devinit esb_probe(struct pci_dev *pdev,
if not reset to the default */
if (heartbeat < 0x1 || heartbeat > 2 * 0x03ff) {
heartbeat = WATCHDOG_HEARTBEAT;
- printk(KERN_INFO PFX
- "heartbeat value must be 1<heartbeat<2046, using %d\n",
- heartbeat);
+ pr_info("heartbeat value must be 1<heartbeat<2046, using %d\n",
+ heartbeat);
}
/* Initialize the watchdog and make sure it does not run */
@@ -450,14 +449,12 @@ static int __devinit esb_probe(struct pci_dev *pdev,
/* Register the watchdog so that userspace has access to it */
ret = misc_register(&esb_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto err_unmap;
}
- printk(KERN_INFO PFX
- "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n",
- BASEADDR, heartbeat, nowayout);
+ pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n",
+ BASEADDR, heartbeat, nowayout);
return 0;
err_unmap:
@@ -503,7 +500,7 @@ static int __init watchdog_init(void)
static void __exit watchdog_cleanup(void)
{
pci_unregister_driver(&esb_driver);
- printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+ pr_info("Watchdog Module Unloaded\n");
}
module_init(watchdog_init);
diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c
index 481d1ad..2721d29 100644
--- a/drivers/watchdog/iTCO_vendor_support.c
+++ b/drivers/watchdog/iTCO_vendor_support.c
@@ -17,10 +17,11 @@
* Includes, defines, variables, module parameters, ...
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
/* Module and version information */
#define DRV_NAME "iTCO_vendor_support"
#define DRV_VERSION "1.04"
-#define PFX DRV_NAME ": "
/* Includes */
#include <linux/module.h> /* For module specific items */
@@ -355,13 +356,13 @@ EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on);
static int __init iTCO_vendor_init_module(void)
{
- printk(KERN_INFO PFX "vendor-support=%d\n", vendorsupport);
+ pr_info("vendor-support=%d\n", vendorsupport);
return 0;
}
static void __exit iTCO_vendor_exit_module(void)
{
- printk(KERN_INFO PFX "Module Unloaded\n");
+ pr_info("Module Unloaded\n");
}
module_init(iTCO_vendor_init_module);
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index bdf401b..9fecb95 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -43,10 +43,11 @@
* Includes, defines, variables, module parameters, ...
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
/* Module and version information */
#define DRV_NAME "iTCO_wdt"
#define DRV_VERSION "1.07"
-#define PFX DRV_NAME ": "
/* Includes */
#include <linux/module.h> /* For module specific items */
@@ -413,8 +414,8 @@ MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. "
"5..76 (TCO v1) or 3..614 (TCO v2), default="
__MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -489,8 +490,7 @@ static int iTCO_wdt_start(void)
/* disable chipset's NO_REBOOT bit */
if (iTCO_wdt_unset_NO_REBOOT_bit()) {
spin_unlock(&iTCO_wdt_private.io_lock);
- printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, "
- "reboot disabled by hardware/BIOS\n");
+ pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n");
return -EIO;
}
@@ -661,8 +661,7 @@ static int iTCO_wdt_release(struct inode *inode, struct file *file)
if (expect_release == 42) {
iTCO_wdt_stop();
} else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
iTCO_wdt_keepalive();
}
clear_bit(0, &is_active);
@@ -804,8 +803,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
base_address &= 0x0000ff80;
if (base_address == 0x00000000) {
/* Something's wrong here, ACPIBASE has to be set */
- printk(KERN_ERR PFX "failed to get TCOBASE address, "
- "device disabled by hardware/BIOS\n");
+ pr_err("failed to get TCOBASE address, device disabled by hardware/BIOS\n");
return -ENODEV;
}
iTCO_wdt_private.iTCO_version =
@@ -820,8 +818,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
if (iTCO_wdt_private.iTCO_version == 2) {
pci_read_config_dword(pdev, 0xf0, &base_address);
if ((base_address & 1) == 0) {
- printk(KERN_ERR PFX "RCBA is disabled by hardware"
- "/BIOS, device disabled\n");
+ pr_err("RCBA is disabled by hardware/BIOS, device disabled\n");
ret = -ENODEV;
goto out;
}
@@ -831,8 +828,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
/* Check chipset's NO_REBOOT bit */
if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
- printk(KERN_INFO PFX "unable to reset NO_REBOOT flag, "
- "device disabled by hardware/BIOS\n");
+ pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
ret = -ENODEV; /* Cannot reset NO_REBOOT bit */
goto out_unmap;
}
@@ -842,9 +838,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
/* The TCO logic uses the TCO_EN bit in the SMI_EN register */
if (!request_region(SMI_EN, 4, "iTCO_wdt")) {
- printk(KERN_ERR PFX
- "I/O address 0x%04lx already in use, "
- "device disabled\n", SMI_EN);
+ pr_err("I/O address 0x%04lx already in use, device disabled\n",
+ SMI_EN);
ret = -EIO;
goto out_unmap;
}
@@ -858,17 +853,16 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
/* The TCO I/O registers reside in a 32-byte range pointed to
by the TCOBASE value */
if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) {
- printk(KERN_ERR PFX "I/O address 0x%04lx already in use "
- "device disabled\n", TCOBASE);
+ pr_err("I/O address 0x%04lx already in use, device disabled\n",
+ TCOBASE);
ret = -EIO;
goto unreg_smi_en;
}
- printk(KERN_INFO PFX
- "Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)\n",
- iTCO_chipset_info[ent->driver_data].name,
- iTCO_chipset_info[ent->driver_data].iTCO_version,
- TCOBASE);
+ pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)\n",
+ iTCO_chipset_info[ent->driver_data].name,
+ iTCO_chipset_info[ent->driver_data].iTCO_version,
+ TCOBASE);
/* Clear out the (probably old) status */
outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
@@ -882,20 +876,18 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
if not reset to the default */
if (iTCO_wdt_set_heartbeat(heartbeat)) {
iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT);
- printk(KERN_INFO PFX
- "timeout value out of range, using %d\n", heartbeat);
+ pr_info("timeout value out of range, using %d\n", heartbeat);
}
ret = misc_register(&iTCO_wdt_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto unreg_region;
}
- printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
- heartbeat, nowayout);
+ pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
+ heartbeat, nowayout);
return 0;
@@ -947,7 +939,7 @@ static int __devinit iTCO_wdt_probe(struct platform_device *dev)
}
if (!found)
- printk(KERN_INFO PFX "No device detected.\n");
+ pr_info("No device detected\n");
return ret;
}
@@ -979,8 +971,7 @@ static int __init iTCO_wdt_init_module(void)
{
int err;
- printk(KERN_INFO PFX "Intel TCO WatchDog Timer Driver v%s\n",
- DRV_VERSION);
+ pr_info("Intel TCO WatchDog Timer Driver v%s\n", DRV_VERSION);
err = platform_driver_register(&iTCO_wdt_driver);
if (err)
@@ -1004,7 +995,7 @@ static void __exit iTCO_wdt_cleanup_module(void)
{
platform_device_unregister(iTCO_wdt_platform_device);
platform_driver_unregister(&iTCO_wdt_driver);
- printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+ pr_info("Watchdog Module Unloaded\n");
}
module_init(iTCO_wdt_init_module);
diff --git a/drivers/watchdog/ib700wdt.c b/drivers/watchdog/ib700wdt.c
index 0149d8d..184c0bf 100644
--- a/drivers/watchdog/ib700wdt.c
+++ b/drivers/watchdog/ib700wdt.c
@@ -31,6 +31,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
@@ -44,7 +46,6 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
static struct platform_device *ibwdt_platform_device;
static unsigned long ibwdt_is_open;
@@ -53,7 +54,6 @@ static char expect_close;
/* Module information */
#define DRV_NAME "ib700wdt"
-#define PFX DRV_NAME ": "
/*
*
@@ -102,8 +102,8 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. 0<= timeout <=30, default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -246,8 +246,7 @@ static int ibwdt_close(struct inode *inode, struct file *file)
if (expect_close == 42) {
ibwdt_disable();
} else {
- printk(KERN_CRIT PFX
- "WDT device closed unexpectedly. WDT will not stop!\n");
+ pr_crit("WDT device closed unexpectedly. WDT will not stop!\n");
ibwdt_ping();
}
clear_bit(0, &ibwdt_is_open);
@@ -284,16 +283,14 @@ static int __devinit ibwdt_probe(struct platform_device *dev)
#if WDT_START != WDT_STOP
if (!request_region(WDT_STOP, 1, "IB700 WDT")) {
- printk(KERN_ERR PFX "STOP method I/O %X is not available.\n",
- WDT_STOP);
+ pr_err("STOP method I/O %X is not available\n", WDT_STOP);
res = -EIO;
goto out_nostopreg;
}
#endif
if (!request_region(WDT_START, 1, "IB700 WDT")) {
- printk(KERN_ERR PFX "START method I/O %X is not available.\n",
- WDT_START);
+ pr_err("START method I/O %X is not available\n", WDT_START);
res = -EIO;
goto out_nostartreg;
}
@@ -302,13 +299,12 @@ static int __devinit ibwdt_probe(struct platform_device *dev)
* if not reset to the default */
if (ibwdt_set_heartbeat(timeout)) {
ibwdt_set_heartbeat(WATCHDOG_TIMEOUT);
- printk(KERN_INFO PFX
- "timeout value must be 0<=x<=30, using %d\n", timeout);
+ pr_info("timeout value must be 0<=x<=30, using %d\n", timeout);
}
res = misc_register(&ibwdt_miscdev);
if (res) {
- printk(KERN_ERR PFX "failed to register misc device\n");
+ pr_err("failed to register misc device\n");
goto out_nomisc;
}
return 0;
@@ -353,8 +349,7 @@ static int __init ibwdt_init(void)
{
int err;
- printk(KERN_INFO PFX
- "WDT driver for IB700 single board computer initialising.\n");
+ pr_info("WDT driver for IB700 single board computer initialising\n");
err = platform_driver_register(&ibwdt_driver);
if (err)
@@ -378,7 +373,7 @@ static void __exit ibwdt_exit(void)
{
platform_device_unregister(ibwdt_platform_device);
platform_driver_unregister(&ibwdt_driver);
- printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+ pr_info("Watchdog Module Unloaded\n");
}
module_init(ibwdt_init);
diff --git a/drivers/watchdog/ibmasr.c b/drivers/watchdog/ibmasr.c
index c7481ad..bc3fb8f 100644
--- a/drivers/watchdog/ibmasr.c
+++ b/drivers/watchdog/ibmasr.c
@@ -10,6 +10,8 @@
* of the GNU Public License, incorporated herein by reference.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -31,8 +33,6 @@ enum {
ASMTYPE_SPRUCE,
};
-#define PFX "ibmasr: "
-
#define TOPAZ_ASR_REG_OFFSET 4
#define TOPAZ_ASR_TOGGLE 0x40
#define TOPAZ_ASR_DISABLE 0x80
@@ -60,7 +60,7 @@ enum {
#define SPRUCE_ASR_TOGGLE_MASK 0x02 /* bit 0: 0, then 1, then 0 */
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static unsigned long asr_is_open;
static char asr_expect_close;
@@ -234,12 +234,11 @@ static int __init asr_get_base_address(void)
}
if (!request_region(asr_base, asr_length, "ibmasr")) {
- printk(KERN_ERR PFX "address %#x already in use\n",
- asr_base);
+ pr_err("address %#x already in use\n", asr_base);
return -EBUSY;
}
- printk(KERN_INFO PFX "found %sASR @ addr %#x\n", type, asr_base);
+ pr_info("found %sASR @ addr %#x\n", type, asr_base);
return 0;
}
@@ -332,8 +331,7 @@ static int asr_release(struct inode *inode, struct file *file)
if (asr_expect_close == 42)
asr_disable();
else {
- printk(KERN_CRIT PFX
- "unexpected close, not stopping watchdog!\n");
+ pr_crit("unexpected close, not stopping watchdog!\n");
asr_toggle();
}
clear_bit(0, &asr_is_open);
@@ -393,7 +391,7 @@ static int __init ibmasr_init(void)
rc = misc_register(&asr_miscdev);
if (rc < 0) {
release_region(asr_base, asr_length);
- printk(KERN_ERR PFX "failed to register misc device\n");
+ pr_err("failed to register misc device\n");
return rc;
}
@@ -413,7 +411,7 @@ static void __exit ibmasr_exit(void)
module_init(ibmasr_init);
module_exit(ibmasr_exit);
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index c44c333..7a2b734 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -46,6 +46,9 @@
#define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */
#define IMX2_WDT_SEQ2 0xAAAA /* -> service sequence 2 */
+#define IMX2_WDT_WRSR 0x04 /* Reset Status Register */
+#define IMX2_WDT_WRSR_TOUT (1 << 1) /* -> Reset due to Timeout */
+
#define IMX2_WDT_MAX_TIME 128
#define IMX2_WDT_DEFAULT_TIME 60 /* in seconds */
@@ -65,8 +68,8 @@ static struct {
static struct miscdevice imx2_wdt_miscdev;
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -175,6 +178,7 @@ static long imx2_wdt_ioctl(struct file *file, unsigned int cmd,
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_value;
+ u16 val;
switch (cmd) {
case WDIOC_GETSUPPORT:
@@ -182,9 +186,13 @@ static long imx2_wdt_ioctl(struct file *file, unsigned int cmd,
sizeof(struct watchdog_info)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
+ case WDIOC_GETBOOTSTATUS:
+ val = __raw_readw(imx2_wdt.base + IMX2_WDT_WRSR);
+ new_value = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0;
+ return put_user(new_value, p);
+
case WDIOC_KEEPALIVE:
imx2_wdt_ping();
return 0;
diff --git a/drivers/watchdog/indydog.c b/drivers/watchdog/indydog.c
index 1475e09..6d90f7a 100644
--- a/drivers/watchdog/indydog.c
+++ b/drivers/watchdog/indydog.c
@@ -12,6 +12,8 @@
* based on softdog.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -26,14 +28,13 @@
#include <linux/uaccess.h>
#include <asm/sgi/mc.h>
-#define PFX "indydog: "
static unsigned long indydog_alive;
static DEFINE_SPINLOCK(indydog_lock);
#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -60,7 +61,7 @@ static void indydog_stop(void)
sgimc->cpuctrl0 = mc_ctrl0;
spin_unlock(&indydog_lock);
- printk(KERN_INFO PFX "Stopped watchdog timer.\n");
+ pr_info("Stopped watchdog timer\n");
}
static void indydog_ping(void)
@@ -83,7 +84,7 @@ static int indydog_open(struct inode *inode, struct file *file)
indydog_start();
indydog_ping();
- printk(KERN_INFO "Started watchdog timer.\n");
+ pr_info("Started watchdog timer\n");
return nonseekable_open(inode, file);
}
@@ -178,30 +179,25 @@ static struct notifier_block indydog_notifier = {
.notifier_call = indydog_notify_sys,
};
-static char banner[] __initdata =
- KERN_INFO PFX "Hardware Watchdog Timer for SGI IP22: 0.3\n";
-
static int __init watchdog_init(void)
{
int ret;
ret = register_reboot_notifier(&indydog_notifier);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
return ret;
}
ret = misc_register(&indydog_miscdev);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
unregister_reboot_notifier(&indydog_notifier);
return ret;
}
- printk(banner);
+ pr_info("Hardware Watchdog Timer for SGI IP22: 0.3\n");
return 0;
}
diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c
index 1abdc04..9dda2d0 100644
--- a/drivers/watchdog/intel_scu_watchdog.c
+++ b/drivers/watchdog/intel_scu_watchdog.c
@@ -22,6 +22,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/compiler.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -96,15 +98,14 @@ static struct intel_scu_watchdog_dev watchdog_device;
static void watchdog_fire(void)
{
if (force_boot) {
- printk(KERN_CRIT PFX "Initiating system reboot.\n");
+ pr_crit("Initiating system reboot\n");
emergency_restart();
- printk(KERN_CRIT PFX "Reboot didn't ?????\n");
+ pr_crit("Reboot didn't ?????\n");
}
else {
- printk(KERN_CRIT PFX "Immediate Reboot Disabled\n");
- printk(KERN_CRIT PFX
- "System will reset when watchdog timer times out!\n");
+ pr_crit("Immediate Reboot Disabled\n");
+ pr_crit("System will reset when watchdog timer times out!\n");
}
}
@@ -112,8 +113,8 @@ static int check_timer_margin(int new_margin)
{
if ((new_margin < MIN_TIME_CYCLE) ||
(new_margin > MAX_TIME - timer_set)) {
- pr_debug("Watchdog timer: value of new_margin %d is out of the range %d to %d\n",
- new_margin, MIN_TIME_CYCLE, MAX_TIME - timer_set);
+ pr_debug("value of new_margin %d is out of the range %d to %d\n",
+ new_margin, MIN_TIME_CYCLE, MAX_TIME - timer_set);
return -EINVAL;
}
return 0;
@@ -156,14 +157,14 @@ static irqreturn_t watchdog_timer_interrupt(int irq, void *dev_id)
int int_status;
int_status = ioread32(watchdog_device.timer_interrupt_status_addr);
- pr_debug("Watchdog timer: irq, int_status: %x\n", int_status);
+ pr_debug("irq, int_status: %x\n", int_status);
if (int_status != 0)
return IRQ_NONE;
/* has the timer been started? If not, then this is spurious */
if (watchdog_device.timer_started == 0) {
- pr_debug("Watchdog timer: spurious interrupt received\n");
+ pr_debug("spurious interrupt received\n");
return IRQ_HANDLED;
}
@@ -220,16 +221,15 @@ static int intel_scu_set_heartbeat(u32 t)
(watchdog_device.timer_set - timer_margin)
* watchdog_device.timer_tbl_ptr->freq_hz;
- pr_debug("Watchdog timer: set_heartbeat: timer freq is %d\n",
- watchdog_device.timer_tbl_ptr->freq_hz);
- pr_debug("Watchdog timer: set_heartbeat: timer_set is %x (hex)\n",
- watchdog_device.timer_set);
- pr_debug("Watchdog timer: set_hearbeat: timer_margin is %x (hex)\n",
- timer_margin);
- pr_debug("Watchdog timer: set_heartbeat: threshold is %x (hex)\n",
- watchdog_device.threshold);
- pr_debug("Watchdog timer: set_heartbeat: soft_threshold is %x (hex)\n",
- watchdog_device.soft_threshold);
+ pr_debug("set_heartbeat: timer freq is %d\n",
+ watchdog_device.timer_tbl_ptr->freq_hz);
+ pr_debug("set_heartbeat: timer_set is %x (hex)\n",
+ watchdog_device.timer_set);
+ pr_debug("set_hearbeat: timer_margin is %x (hex)\n", timer_margin);
+ pr_debug("set_heartbeat: threshold is %x (hex)\n",
+ watchdog_device.threshold);
+ pr_debug("set_heartbeat: soft_threshold is %x (hex)\n",
+ watchdog_device.soft_threshold);
/* Adjust thresholds by FREQ_ADJUSTMENT factor, to make the */
/* watchdog timing come out right. */
@@ -264,7 +264,7 @@ static int intel_scu_set_heartbeat(u32 t)
if (MAX_RETRY < retry_count++) {
/* Unable to set timer value */
- pr_err("Watchdog timer: Unable to set timer\n");
+ pr_err("Unable to set timer\n");
return -ENODEV;
}
@@ -321,18 +321,17 @@ static int intel_scu_release(struct inode *inode, struct file *file)
*/
if (!test_and_clear_bit(0, &watchdog_device.driver_open)) {
- pr_debug("Watchdog timer: intel_scu_release, without open\n");
+ pr_debug("intel_scu_release, without open\n");
return -ENOTTY;
}
if (!watchdog_device.timer_started) {
/* Just close, since timer has not been started */
- pr_debug("Watchdog timer: closed, without starting timer\n");
+ pr_debug("closed, without starting timer\n");
return 0;
}
- printk(KERN_CRIT PFX
- "Unexpected close of /dev/watchdog!\n");
+ pr_crit("Unexpected close of /dev/watchdog!\n");
/* Since the timer was started, prevent future reopens */
watchdog_device.driver_closed = 1;
@@ -454,9 +453,8 @@ static int __init intel_scu_watchdog_init(void)
/* Check value of timer_set boot parameter */
if ((timer_set < MIN_TIME_CYCLE) ||
(timer_set > MAX_TIME - MIN_TIME_CYCLE)) {
- pr_err("Watchdog timer: value of timer_set %x (hex) "
- "is out of range from %x to %x (hex)\n",
- timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE);
+ pr_err("value of timer_set %x (hex) is out of range from %x to %x (hex)\n",
+ timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE);
return -EINVAL;
}
@@ -467,19 +465,18 @@ static int __init intel_scu_watchdog_init(void)
watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
if (watchdog_device.timer_tbl_ptr == NULL) {
- pr_debug("Watchdog timer - Intel SCU watchdog: timer is not available\n");
+ pr_debug("timer is not available\n");
return -ENODEV;
}
/* make sure the timer exists */
if (watchdog_device.timer_tbl_ptr->phys_addr == 0) {
- pr_debug("Watchdog timer - Intel SCU watchdog - timer %d does not have valid physical memory\n",
- sfi_mtimer_num);
+ pr_debug("timer %d does not have valid physical memory\n",
+ sfi_mtimer_num);
return -ENODEV;
}
if (watchdog_device.timer_tbl_ptr->irq == 0) {
- pr_debug("Watchdog timer: timer %d invalid irq\n",
- sfi_mtimer_num);
+ pr_debug("timer %d invalid irq\n", sfi_mtimer_num);
return -ENODEV;
}
@@ -487,7 +484,7 @@ static int __init intel_scu_watchdog_init(void)
20);
if (tmp_addr == NULL) {
- pr_debug("Watchdog timer: timer unable to ioremap\n");
+ pr_debug("timer unable to ioremap\n");
return -ENOMEM;
}
@@ -512,7 +509,7 @@ static int __init intel_scu_watchdog_init(void)
ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier);
if (ret) {
- pr_err("Watchdog timer: cannot register notifier %d)\n", ret);
+ pr_err("cannot register notifier %d)\n", ret);
goto register_reboot_error;
}
@@ -522,8 +519,8 @@ static int __init intel_scu_watchdog_init(void)
ret = misc_register(&watchdog_device.miscdev);
if (ret) {
- pr_err("Watchdog timer: cannot register miscdev %d err =%d\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev %d err =%d\n",
+ WATCHDOG_MINOR, ret);
goto misc_register_error;
}
@@ -532,7 +529,7 @@ static int __init intel_scu_watchdog_init(void)
IRQF_SHARED, "watchdog",
&watchdog_device.timer_load_count_addr);
if (ret) {
- pr_err("Watchdog timer: error requesting irq %d\n", ret);
+ pr_err("error requesting irq %d\n", ret);
goto request_irq_error;
}
/* Make sure timer is disabled before returning */
diff --git a/drivers/watchdog/intel_scu_watchdog.h b/drivers/watchdog/intel_scu_watchdog.h
index d2b074a..f3ac608 100644
--- a/drivers/watchdog/intel_scu_watchdog.h
+++ b/drivers/watchdog/intel_scu_watchdog.h
@@ -25,7 +25,6 @@
#ifndef __INTEL_SCU_WATCHDOG_H
#define __INTEL_SCU_WATCHDOG_H
-#define PFX "Intel_SCU: "
#define WDT_VER "0.3"
/* minimum time between interrupts */
diff --git a/drivers/watchdog/iop_wdt.c b/drivers/watchdog/iop_wdt.c
index 82fa7a9..d964faf 100644
--- a/drivers/watchdog/iop_wdt.c
+++ b/drivers/watchdog/iop_wdt.c
@@ -24,6 +24,8 @@
* Dan Williams <dan.j.williams@intel.com>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
@@ -34,7 +36,7 @@
#include <linux/uaccess.h>
#include <mach/hardware.h>
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static unsigned long wdt_status;
static unsigned long boot_status;
static DEFINE_SPINLOCK(wdt_lock);
@@ -85,7 +87,7 @@ static int wdt_disable(void)
write_wdtcr(IOP_WDTCR_DIS);
clear_bit(WDT_ENABLED, &wdt_status);
spin_unlock(&wdt_lock);
- printk(KERN_INFO "WATCHDOG: Disabled\n");
+ pr_info("Disabled\n");
return 0;
} else
return 1;
@@ -197,8 +199,8 @@ static int iop_wdt_release(struct inode *inode, struct file *file)
*/
if (state != 0) {
wdt_enable();
- printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
- "reset in %lu seconds\n", iop_watchdog_timeout());
+ pr_crit("Device closed unexpectedly - reset in %lu seconds\n",
+ iop_watchdog_timeout());
}
clear_bit(WDT_IN_USE, &wdt_status);
@@ -238,8 +240,7 @@ static int __init iop_wdt_init(void)
with an open */
ret = misc_register(&iop_wdt_miscdev);
if (ret == 0)
- printk(KERN_INFO "iop watchdog timer: timeout %lu sec\n",
- iop_watchdog_timeout());
+ pr_info("timeout %lu sec\n", iop_watchdog_timeout());
return ret;
}
@@ -252,7 +253,7 @@ static void __exit iop_wdt_exit(void)
module_init(iop_wdt_init);
module_exit(iop_wdt_exit);
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>");
diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c
index 8d2d850..f4cce6d 100644
--- a/drivers/watchdog/it8712f_wdt.c
+++ b/drivers/watchdog/it8712f_wdt.c
@@ -20,6 +20,8 @@
* software is provided AS-IS with no warranties.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -33,6 +35,7 @@
#include <linux/io.h>
#include <linux/ioport.h>
+#define DEBUG
#define NAME "it8712f_wdt"
MODULE_AUTHOR("Jorge Boncompte - DTI2 <jorge@dti2.net>");
@@ -45,8 +48,8 @@ static int margin = 60; /* in seconds */
module_param(margin, int, 0);
MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
static unsigned long wdt_open;
@@ -158,10 +161,10 @@ static void it8712f_wdt_update_margin(void)
*/
if (units <= max_units) {
config |= WDT_UNIT_SEC; /* else UNIT is MINUTES */
- printk(KERN_INFO NAME ": timer margin %d seconds\n", units);
+ pr_info("timer margin %d seconds\n", units);
} else {
units /= 60;
- printk(KERN_INFO NAME ": timer margin %d minutes\n", units);
+ pr_info("timer margin %d minutes\n", units);
}
superio_outb(config, WDT_CONFIG);
@@ -184,7 +187,7 @@ static int it8712f_wdt_enable(void)
if (ret)
return ret;
- printk(KERN_DEBUG NAME ": enabling watchdog timer\n");
+ pr_debug("enabling watchdog timer\n");
superio_select(LDN_GPIO);
superio_outb(wdt_control_reg, WDT_CONTROL);
@@ -204,7 +207,7 @@ static int it8712f_wdt_disable(void)
if (ret)
return ret;
- printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
+ pr_debug("disabling watchdog timer\n");
superio_select(LDN_GPIO);
superio_outb(0, WDT_CONFIG);
@@ -331,12 +334,10 @@ static int it8712f_wdt_open(struct inode *inode, struct file *file)
static int it8712f_wdt_release(struct inode *inode, struct file *file)
{
if (expect_close != 42) {
- printk(KERN_WARNING NAME
- ": watchdog device closed unexpectedly, will not"
- " disable the watchdog timer\n");
+ pr_warn("watchdog device closed unexpectedly, will not disable the watchdog timer\n");
} else if (!nowayout) {
if (it8712f_wdt_disable())
- printk(KERN_WARNING NAME "Watchdog disable failed\n");
+ pr_warn("Watchdog disable failed\n");
}
expect_close = 0;
clear_bit(0, &wdt_open);
@@ -374,13 +375,13 @@ static int __init it8712f_wdt_find(unsigned short *address)
superio_select(LDN_GAME);
superio_outb(1, ACT_REG);
if (!(superio_inb(ACT_REG) & 0x01)) {
- printk(KERN_ERR NAME ": Device not activated, skipping\n");
+ pr_err("Device not activated, skipping\n");
goto exit;
}
*address = superio_inw(BASE_REG);
if (*address == 0) {
- printk(KERN_ERR NAME ": Base address not set, skipping\n");
+ pr_err("Base address not set, skipping\n");
goto exit;
}
@@ -394,8 +395,7 @@ static int __init it8712f_wdt_find(unsigned short *address)
if (margin > (max_units * 60))
margin = (max_units * 60);
- printk(KERN_INFO NAME ": Found IT%04xF chip revision %d - "
- "using DogFood address 0x%x\n",
+ pr_info("Found IT%04xF chip revision %d - using DogFood address 0x%x\n",
chip_type, revision, *address);
exit:
@@ -411,27 +411,26 @@ static int __init it8712f_wdt_init(void)
return -ENODEV;
if (!request_region(address, 1, "IT8712F Watchdog")) {
- printk(KERN_WARNING NAME ": watchdog I/O region busy\n");
+ pr_warn("watchdog I/O region busy\n");
return -EBUSY;
}
err = it8712f_wdt_disable();
if (err) {
- printk(KERN_ERR NAME ": unable to disable watchdog timer.\n");
+ pr_err("unable to disable watchdog timer\n");
goto out;
}
err = register_reboot_notifier(&it8712f_wdt_notifier);
if (err) {
- printk(KERN_ERR NAME ": unable to register reboot notifier\n");
+ pr_err("unable to register reboot notifier\n");
goto out;
}
err = misc_register(&it8712f_wdt_miscdev);
if (err) {
- printk(KERN_ERR NAME
- ": cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, err);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, err);
goto reboot_out;
}
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c
index a2d9a12..8a741bc 100644
--- a/drivers/watchdog/it87_wdt.c
+++ b/drivers/watchdog/it87_wdt.c
@@ -29,6 +29,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -43,11 +45,9 @@
#include <linux/uaccess.h>
#include <linux/io.h>
-#include <asm/system.h>
#define WATCHDOG_VERSION "1.14"
#define WATCHDOG_NAME "IT87 WDT"
-#define PFX WATCHDOG_NAME ": "
#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
#define WD_MAGIC 'V'
@@ -142,7 +142,7 @@ static int nogameport = DEFAULT_NOGAMEPORT;
static int exclusive = DEFAULT_EXCLUSIVE;
static int timeout = DEFAULT_TIMEOUT;
static int testmode = DEFAULT_TESTMODE;
-static int nowayout = DEFAULT_NOWAYOUT;
+static bool nowayout = DEFAULT_NOWAYOUT;
module_param(nogameport, int, 0);
MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default="
@@ -156,7 +156,7 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
module_param(testmode, int, 0);
MODULE_PARM_DESC(testmode, "Watchdog test mode (1 = no reboot), default="
__MODULE_STRING(DEFAULT_TESTMODE));
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="
__MODULE_STRING(WATCHDOG_NOWAYOUT));
@@ -428,8 +428,7 @@ static int wdt_release(struct inode *inode, struct file *file)
clear_bit(WDTS_TIMER_RUN, &wdt_status);
} else {
wdt_keepalive();
- printk(KERN_CRIT PFX
- "unexpected close, not stopping watchdog!\n");
+ pr_crit("unexpected close, not stopping watchdog!\n");
}
}
clear_bit(WDTS_DEV_OPEN, &wdt_status);
@@ -621,16 +620,14 @@ static int __init it87_wdt_init(void)
try_gameport = 0;
break;
case IT8705_ID:
- printk(KERN_ERR PFX
- "Unsupported Chip found, Chip %04x Revision %02x\n",
+ pr_err("Unsupported Chip found, Chip %04x Revision %02x\n",
chip_type, chip_rev);
return -ENODEV;
case NO_DEV_ID:
- printk(KERN_ERR PFX "no device\n");
+ pr_err("no device\n");
return -ENODEV;
default:
- printk(KERN_ERR PFX
- "Unknown Chip found, Chip %04x Revision %04x\n",
+ pr_err("Unknown Chip found, Chip %04x Revision %04x\n",
chip_type, chip_rev);
return -ENODEV;
}
@@ -663,13 +660,11 @@ static int __init it87_wdt_init(void)
if (!test_bit(WDTS_USE_GP, &wdt_status)) {
if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
if (gp_rreq_fail)
- printk(KERN_ERR PFX
- "I/O Address 0x%04x and 0x%04x"
- " already in use\n", base, CIR_BASE);
+ pr_err("I/O Address 0x%04x and 0x%04x already in use\n",
+ base, CIR_BASE);
else
- printk(KERN_ERR PFX
- "I/O Address 0x%04x already in use\n",
- CIR_BASE);
+ pr_err("I/O Address 0x%04x already in use\n",
+ CIR_BASE);
rc = -EIO;
goto err_out;
}
@@ -688,9 +683,8 @@ static int __init it87_wdt_init(void)
if (timeout < 1 || timeout > max_units * 60) {
timeout = DEFAULT_TIMEOUT;
- printk(KERN_WARNING PFX
- "Timeout value out of range, use default %d sec\n",
- DEFAULT_TIMEOUT);
+ pr_warn("Timeout value out of range, use default %d sec\n",
+ DEFAULT_TIMEOUT);
}
if (timeout > max_units)
@@ -698,16 +692,14 @@ static int __init it87_wdt_init(void)
rc = register_reboot_notifier(&wdt_notifier);
if (rc) {
- printk(KERN_ERR PFX
- "Cannot register reboot notifier (err=%d)\n", rc);
+ pr_err("Cannot register reboot notifier (err=%d)\n", rc);
goto err_out_region;
}
rc = misc_register(&wdt_miscdev);
if (rc) {
- printk(KERN_ERR PFX
- "Cannot register miscdev on minor=%d (err=%d)\n",
- wdt_miscdev.minor, rc);
+ pr_err("Cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
goto err_out_reboot;
}
@@ -722,9 +714,8 @@ static int __init it87_wdt_init(void)
outb(0x09, CIR_IER(base));
}
- printk(KERN_INFO PFX "Chip IT%04x revision %d initialized. "
- "timeout=%d sec (nowayout=%d testmode=%d exclusive=%d "
- "nogameport=%d)\n", chip_type, chip_rev, timeout,
+ pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d exclusive=%d nogameport=%d)\n",
+ chip_type, chip_rev, timeout,
nowayout, testmode, exclusive, nogameport);
superio_exit();
diff --git a/drivers/watchdog/ixp2000_wdt.c b/drivers/watchdog/ixp2000_wdt.c
index 084f71aa..3f047a5 100644
--- a/drivers/watchdog/ixp2000_wdt.c
+++ b/drivers/watchdog/ixp2000_wdt.c
@@ -16,6 +16,8 @@
* warranty of any kind, whether express or implied.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -29,7 +31,7 @@
#include <linux/uaccess.h>
#include <mach/hardware.h>
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static unsigned int heartbeat = 60; /* (secs) Default is 1 minute */
static unsigned long wdt_status;
static DEFINE_SPINLOCK(wdt_lock);
@@ -158,8 +160,7 @@ static int ixp2000_wdt_release(struct inode *inode, struct file *file)
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
wdt_disable();
else
- printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
- "timer will not stop\n");
+ pr_crit("Device closed unexpectedly - timer will not stop\n");
clear_bit(WDT_IN_USE, &wdt_status);
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
@@ -185,7 +186,7 @@ static struct miscdevice ixp2000_wdt_miscdev = {
static int __init ixp2000_wdt_init(void)
{
if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) {
- printk(KERN_INFO "Unable to use IXP2000 watchdog due to IXP2800 erratum #25.\n");
+ pr_info("Unable to use IXP2000 watchdog due to IXP2800 erratum #25\n");
return -EIO;
}
wdt_tick_rate = (*IXP2000_T1_CLD * HZ) / 256;
@@ -206,7 +207,7 @@ MODULE_DESCRIPTION("IXP2000 Network Processor Watchdog");
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c
index 4fc2e9a..5580b4f 100644
--- a/drivers/watchdog/ixp4xx_wdt.c
+++ b/drivers/watchdog/ixp4xx_wdt.c
@@ -13,6 +13,8 @@
* warranty of any kind, whether express or implied.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -25,7 +27,7 @@
#include <linux/uaccess.h>
#include <mach/hardware.h>
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = 60; /* (secs) Default is 1 minute */
static unsigned long wdt_status;
static unsigned long boot_status;
@@ -147,8 +149,7 @@ static int ixp4xx_wdt_release(struct inode *inode, struct file *file)
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
wdt_disable();
else
- printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
- "timer will not stop\n");
+ pr_crit("Device closed unexpectedly - timer will not stop\n");
clear_bit(WDT_IN_USE, &wdt_status);
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
@@ -176,8 +177,7 @@ static int __init ixp4xx_wdt_init(void)
int ret;
if (!(read_cpuid_id() & 0xf) && !cpu_is_ixp46x()) {
- printk(KERN_ERR "IXP4XXX Watchdog: Rev. A0 IXP42x CPU detected"
- " - watchdog disabled\n");
+ pr_err("Rev. A0 IXP42x CPU detected - watchdog disabled\n");
return -ENODEV;
}
@@ -185,8 +185,7 @@ static int __init ixp4xx_wdt_init(void)
WDIOF_CARDRESET : 0;
ret = misc_register(&ixp4xx_wdt_miscdev);
if (ret == 0)
- printk(KERN_INFO "IXP4xx Watchdog Timer: heartbeat %d sec\n",
- heartbeat);
+ pr_info("timer heartbeat %d sec\n", heartbeat);
return ret;
}
@@ -205,7 +204,7 @@ MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog");
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
index 17ef300..978615e 100644
--- a/drivers/watchdog/jz4740_wdt.c
+++ b/drivers/watchdog/jz4740_wdt.c
@@ -17,18 +17,15 @@
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/init.h>
-#include <linux/bitops.h>
#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/clk.h>
#include <linux/slab.h>
+#include <linux/err.h>
#include <asm/mach-jz4740/timer.h>
@@ -41,9 +38,6 @@
#define JZ_WDT_CLOCK_RTC 0x2
#define JZ_WDT_CLOCK_EXT 0x4
-#define WDT_IN_USE 0
-#define WDT_OK_TO_CLOSE 1
-
#define JZ_WDT_CLOCK_DIV_SHIFT 3
#define JZ_WDT_CLOCK_DIV_1 (0 << JZ_WDT_CLOCK_DIV_SHIFT)
@@ -56,32 +50,44 @@
#define DEFAULT_HEARTBEAT 5
#define MAX_HEARTBEAT 2048
-static struct {
- void __iomem *base;
- struct resource *mem;
- struct clk *rtc_clk;
- unsigned long status;
-} jz4740_wdt;
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-static int heartbeat = DEFAULT_HEARTBEAT;
+static unsigned int heartbeat = DEFAULT_HEARTBEAT;
+module_param(heartbeat, uint, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat period in seconds from 1 to "
+ __MODULE_STRING(MAX_HEARTBEAT) ", default "
+ __MODULE_STRING(DEFAULT_HEARTBEAT));
+struct jz4740_wdt_drvdata {
+ struct watchdog_device wdt;
+ void __iomem *base;
+ struct clk *rtc_clk;
+};
-static void jz4740_wdt_service(void)
+static int jz4740_wdt_ping(struct watchdog_device *wdt_dev)
{
- writew(0x0, jz4740_wdt.base + JZ_REG_WDT_TIMER_COUNTER);
+ struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
+
+ writew(0x0, drvdata->base + JZ_REG_WDT_TIMER_COUNTER);
+ return 0;
}
-static void jz4740_wdt_set_heartbeat(int new_heartbeat)
+static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int new_timeout)
{
+ struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
unsigned int rtc_clk_rate;
unsigned int timeout_value;
unsigned short clock_div = JZ_WDT_CLOCK_DIV_1;
- heartbeat = new_heartbeat;
-
- rtc_clk_rate = clk_get_rate(jz4740_wdt.rtc_clk);
+ rtc_clk_rate = clk_get_rate(drvdata->rtc_clk);
- timeout_value = rtc_clk_rate * heartbeat;
+ timeout_value = rtc_clk_rate * new_timeout;
while (timeout_value > 0xffff) {
if (clock_div == JZ_WDT_CLOCK_DIV_1024) {
/* Requested timeout too high;
@@ -93,199 +99,115 @@ static void jz4740_wdt_set_heartbeat(int new_heartbeat)
clock_div += (1 << JZ_WDT_CLOCK_DIV_SHIFT);
}
- writeb(0x0, jz4740_wdt.base + JZ_REG_WDT_COUNTER_ENABLE);
- writew(clock_div, jz4740_wdt.base + JZ_REG_WDT_TIMER_CONTROL);
+ writeb(0x0, drvdata->base + JZ_REG_WDT_COUNTER_ENABLE);
+ writew(clock_div, drvdata->base + JZ_REG_WDT_TIMER_CONTROL);
- writew((u16)timeout_value, jz4740_wdt.base + JZ_REG_WDT_TIMER_DATA);
- writew(0x0, jz4740_wdt.base + JZ_REG_WDT_TIMER_COUNTER);
+ writew((u16)timeout_value, drvdata->base + JZ_REG_WDT_TIMER_DATA);
+ writew(0x0, drvdata->base + JZ_REG_WDT_TIMER_COUNTER);
writew(clock_div | JZ_WDT_CLOCK_RTC,
- jz4740_wdt.base + JZ_REG_WDT_TIMER_CONTROL);
+ drvdata->base + JZ_REG_WDT_TIMER_CONTROL);
- writeb(0x1, jz4740_wdt.base + JZ_REG_WDT_COUNTER_ENABLE);
-}
+ writeb(0x1, drvdata->base + JZ_REG_WDT_COUNTER_ENABLE);
-static void jz4740_wdt_enable(void)
-{
- jz4740_timer_enable_watchdog();
- jz4740_wdt_set_heartbeat(heartbeat);
-}
-
-static void jz4740_wdt_disable(void)
-{
- jz4740_timer_disable_watchdog();
- writeb(0x0, jz4740_wdt.base + JZ_REG_WDT_COUNTER_ENABLE);
+ wdt_dev->timeout = new_timeout;
+ return 0;
}
-static int jz4740_wdt_open(struct inode *inode, struct file *file)
+static int jz4740_wdt_start(struct watchdog_device *wdt_dev)
{
- if (test_and_set_bit(WDT_IN_USE, &jz4740_wdt.status))
- return -EBUSY;
-
- jz4740_wdt_enable();
+ jz4740_timer_enable_watchdog();
+ jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
- return nonseekable_open(inode, file);
+ return 0;
}
-static ssize_t jz4740_wdt_write(struct file *file, const char *data,
- size_t len, loff_t *ppos)
+static int jz4740_wdt_stop(struct watchdog_device *wdt_dev)
{
- if (len) {
- size_t i;
-
- clear_bit(WDT_OK_TO_CLOSE, &jz4740_wdt.status);
- for (i = 0; i != len; i++) {
- char c;
+ struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
- if (get_user(c, data + i))
- return -EFAULT;
-
- if (c == 'V')
- set_bit(WDT_OK_TO_CLOSE, &jz4740_wdt.status);
- }
- jz4740_wdt_service();
- }
+ jz4740_timer_disable_watchdog();
+ writeb(0x0, drvdata->base + JZ_REG_WDT_COUNTER_ENABLE);
- return len;
+ return 0;
}
-static const struct watchdog_info ident = {
- .options = WDIOF_KEEPALIVEPING,
+static const struct watchdog_info jz4740_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "jz4740 Watchdog",
};
-static long jz4740_wdt_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int ret = -ENOTTY;
- int heartbeat_seconds;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = copy_to_user((struct watchdog_info *)arg, &ident,
- sizeof(ident)) ? -EFAULT : 0;
- break;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- ret = put_user(0, (int *)arg);
- break;
-
- case WDIOC_KEEPALIVE:
- jz4740_wdt_service();
- return 0;
-
- case WDIOC_SETTIMEOUT:
- if (get_user(heartbeat_seconds, (int __user *)arg))
- return -EFAULT;
-
- jz4740_wdt_set_heartbeat(heartbeat_seconds);
- return 0;
-
- case WDIOC_GETTIMEOUT:
- return put_user(heartbeat, (int *)arg);
-
- default:
- break;
- }
-
- return ret;
-}
-
-static int jz4740_wdt_release(struct inode *inode, struct file *file)
-{
- jz4740_wdt_service();
-
- if (test_and_clear_bit(WDT_OK_TO_CLOSE, &jz4740_wdt.status))
- jz4740_wdt_disable();
-
- clear_bit(WDT_IN_USE, &jz4740_wdt.status);
- return 0;
-}
-
-static const struct file_operations jz4740_wdt_fops = {
+static const struct watchdog_ops jz4740_wdt_ops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = jz4740_wdt_write,
- .unlocked_ioctl = jz4740_wdt_ioctl,
- .open = jz4740_wdt_open,
- .release = jz4740_wdt_release,
-};
-
-static struct miscdevice jz4740_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &jz4740_wdt_fops,
+ .start = jz4740_wdt_start,
+ .stop = jz4740_wdt_stop,
+ .ping = jz4740_wdt_ping,
+ .set_timeout = jz4740_wdt_set_timeout,
};
static int __devinit jz4740_wdt_probe(struct platform_device *pdev)
{
- int ret = 0, size;
- struct resource *res;
- struct device *dev = &pdev->dev;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(dev, "failed to get memory region resource\n");
- return -ENXIO;
+ struct jz4740_wdt_drvdata *drvdata;
+ struct watchdog_device *jz4740_wdt;
+ struct resource *res;
+ int ret;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(struct jz4740_wdt_drvdata),
+ GFP_KERNEL);
+ if (!drvdata) {
+ dev_err(&pdev->dev, "Unable to alloacate watchdog device\n");
+ return -ENOMEM;
}
- size = resource_size(res);
- jz4740_wdt.mem = request_mem_region(res->start, size, pdev->name);
- if (jz4740_wdt.mem == NULL) {
- dev_err(dev, "failed to get memory region\n");
- return -EBUSY;
- }
+ if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
+ heartbeat = DEFAULT_HEARTBEAT;
- jz4740_wdt.base = ioremap_nocache(res->start, size);
- if (jz4740_wdt.base == NULL) {
- dev_err(dev, "failed to map memory region\n");
+ jz4740_wdt = &drvdata->wdt;
+ jz4740_wdt->info = &jz4740_wdt_info;
+ jz4740_wdt->ops = &jz4740_wdt_ops;
+ jz4740_wdt->timeout = heartbeat;
+ jz4740_wdt->min_timeout = 1;
+ jz4740_wdt->max_timeout = MAX_HEARTBEAT;
+ watchdog_set_nowayout(jz4740_wdt, nowayout);
+ watchdog_set_drvdata(jz4740_wdt, drvdata);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ drvdata->base = devm_request_and_ioremap(&pdev->dev, res);
+ if (drvdata->base == NULL) {
ret = -EBUSY;
- goto err_release_region;
+ goto err_out;
}
- jz4740_wdt.rtc_clk = clk_get(NULL, "rtc");
- if (IS_ERR(jz4740_wdt.rtc_clk)) {
- dev_err(dev, "cannot find RTC clock\n");
- ret = PTR_ERR(jz4740_wdt.rtc_clk);
- goto err_iounmap;
+ drvdata->rtc_clk = clk_get(NULL, "rtc");
+ if (IS_ERR(drvdata->rtc_clk)) {
+ dev_err(&pdev->dev, "cannot find RTC clock\n");
+ ret = PTR_ERR(drvdata->rtc_clk);
+ goto err_out;
}
- ret = misc_register(&jz4740_wdt_miscdev);
- if (ret < 0) {
- dev_err(dev, "cannot register misc device\n");
+ ret = watchdog_register_device(&drvdata->wdt);
+ if (ret < 0)
goto err_disable_clk;
- }
+ platform_set_drvdata(pdev, drvdata);
return 0;
err_disable_clk:
- clk_put(jz4740_wdt.rtc_clk);
-err_iounmap:
- iounmap(jz4740_wdt.base);
-err_release_region:
- release_mem_region(jz4740_wdt.mem->start,
- resource_size(jz4740_wdt.mem));
+ clk_put(drvdata->rtc_clk);
+err_out:
return ret;
}
-
static int __devexit jz4740_wdt_remove(struct platform_device *pdev)
{
- jz4740_wdt_disable();
- misc_deregister(&jz4740_wdt_miscdev);
- clk_put(jz4740_wdt.rtc_clk);
+ struct jz4740_wdt_drvdata *drvdata = platform_get_drvdata(pdev);
- iounmap(jz4740_wdt.base);
- jz4740_wdt.base = NULL;
-
- release_mem_region(jz4740_wdt.mem->start,
- resource_size(jz4740_wdt.mem));
- jz4740_wdt.mem = NULL;
+ jz4740_wdt_stop(&drvdata->wdt);
+ watchdog_unregister_device(&drvdata->wdt);
+ clk_put(drvdata->rtc_clk);
return 0;
}
-
static struct platform_driver jz4740_wdt_driver = {
.probe = jz4740_wdt_probe,
.remove = __devexit_p(jz4740_wdt_remove),
@@ -299,13 +221,6 @@ module_platform_driver(jz4740_wdt_driver);
MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
MODULE_DESCRIPTION("jz4740 Watchdog Driver");
-
-module_param(heartbeat, int, 0);
-MODULE_PARM_DESC(heartbeat,
- "Watchdog heartbeat period in seconds from 1 to "
- __MODULE_STRING(MAX_HEARTBEAT) ", default "
- __MODULE_STRING(DEFAULT_HEARTBEAT));
-
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:jz4740-wdt");
diff --git a/drivers/watchdog/ks8695_wdt.c b/drivers/watchdog/ks8695_wdt.c
index 51757a5..59e75d9 100644
--- a/drivers/watchdog/ks8695_wdt.c
+++ b/drivers/watchdog/ks8695_wdt.c
@@ -8,6 +8,8 @@
* published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -28,14 +30,14 @@
#define WDT_MAX_TIME 171 /* seconds */
static int wdt_time = WDT_DEFAULT_TIME;
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(wdt_time, int, 0);
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
__MODULE_STRING(WDT_DEFAULT_TIME) ")");
#ifdef CONFIG_WATCHDOG_NOWAYOUT
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
#endif
@@ -233,8 +235,8 @@ static int __devinit ks8695wdt_probe(struct platform_device *pdev)
if (res)
return res;
- printk(KERN_INFO "KS8695 Watchdog Timer enabled (%d seconds%s)\n",
- wdt_time, nowayout ? ", nowayout" : "");
+ pr_info("KS8695 Watchdog Timer enabled (%d seconds%s)\n",
+ wdt_time, nowayout ? ", nowayout" : "");
return 0;
}
diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c
index d3a63be..a9593a3 100644
--- a/drivers/watchdog/lantiq_wdt.c
+++ b/drivers/watchdog/lantiq_wdt.c
@@ -7,6 +7,8 @@
* Based on EP93xx wdt driver
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
@@ -38,7 +40,7 @@
#define LTQ_WDT_DIVIDER 0x40000
#define LTQ_MAX_TIMEOUT ((1 << 16) - 1) /* the reload field is 16 bit */
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static void __iomem *ltq_wdt_membase;
static unsigned long ltq_io_region_clk_rate;
@@ -160,7 +162,7 @@ ltq_wdt_release(struct inode *inode, struct file *file)
if (ltq_wdt_ok_to_close)
ltq_wdt_disable();
else
- pr_err("ltq_wdt: watchdog closed without warning\n");
+ pr_err("watchdog closed without warning\n");
ltq_wdt_ok_to_close = 0;
clear_bit(0, &ltq_wdt_in_use);
@@ -249,7 +251,7 @@ exit_ltq_wdt(void)
module_init(init_ltq_wdt);
module_exit(exit_ltq_wdt);
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
diff --git a/drivers/watchdog/m54xx_wdt.c b/drivers/watchdog/m54xx_wdt.c
index 4d43286..663cad8 100644
--- a/drivers/watchdog/m54xx_wdt.c
+++ b/drivers/watchdog/m54xx_wdt.c
@@ -16,6 +16,8 @@
* warranty of any kind, whether express or implied.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -32,7 +34,7 @@
#include <asm/m54xxsim.h>
#include <asm/m54xxgpt.h>
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static unsigned int heartbeat = 30; /* (secs) Default is 0.5 minute */
static unsigned long wdt_status;
@@ -166,8 +168,7 @@ static int m54xx_wdt_release(struct inode *inode, struct file *file)
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
wdt_disable();
else {
- printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
- "timer will not stop\n");
+ pr_crit("Device closed unexpectedly - timer will not stop\n");
wdt_keepalive();
}
clear_bit(WDT_IN_USE, &wdt_status);
@@ -196,11 +197,10 @@ static int __init m54xx_wdt_init(void)
{
if (!request_mem_region(MCF_MBAR + MCF_GPT_GCIR0, 4,
"Coldfire M54xx Watchdog")) {
- printk(KERN_WARNING
- "Coldfire M54xx Watchdog : I/O region busy\n");
+ pr_warn("I/O region busy\n");
return -EBUSY;
}
- printk(KERN_INFO "ColdFire watchdog driver is loaded.\n");
+ pr_info("driver is loaded\n");
return misc_register(&m54xx_wdt_miscdev);
}
@@ -220,7 +220,7 @@ MODULE_DESCRIPTION("Coldfire M54xx Watchdog");
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 30s)");
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c
index 1332b83..bf84f78 100644
--- a/drivers/watchdog/machzwd.c
+++ b/drivers/watchdog/machzwd.c
@@ -28,6 +28,8 @@
* Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -43,7 +45,6 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
/* ports */
#define ZF_IOBASE 0x218
@@ -93,8 +94,8 @@ MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -141,10 +142,10 @@ static unsigned long next_heartbeat;
#define ZF_CTIMEOUT 0xffff
#ifndef ZF_DEBUG
-# define dprintk(format, args...)
+#define dprintk(format, args...)
#else
-# define dprintk(format, args...) printk(KERN_DEBUG PFX \
- ":%s:%d: " format, __func__, __LINE__ , ## args)
+#define dprintk(format, args...) \
+ pr_debug(":%s:%d: " format, __func__, __LINE__ , ## args)
#endif
@@ -203,7 +204,7 @@ static void zf_timer_off(void)
zf_set_control(ctrl_reg);
spin_unlock_irqrestore(&zf_port_lock, flags);
- printk(KERN_INFO PFX ": Watchdog timer is now disabled\n");
+ pr_info("Watchdog timer is now disabled\n");
}
@@ -233,7 +234,7 @@ static void zf_timer_on(void)
zf_set_control(ctrl_reg);
spin_unlock_irqrestore(&zf_port_lock, flags);
- printk(KERN_INFO PFX ": Watchdog timer is now enabled\n");
+ pr_info("Watchdog timer is now enabled\n");
}
@@ -263,7 +264,7 @@ static void zf_ping(unsigned long data)
mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO);
} else
- printk(KERN_CRIT PFX ": I will reset your machine\n");
+ pr_crit("I will reset your machine\n");
}
static ssize_t zf_write(struct file *file, const char __user *buf, size_t count,
@@ -342,8 +343,7 @@ static int zf_close(struct inode *inode, struct file *file)
zf_timer_off();
else {
del_timer(&zf_timer);
- printk(KERN_ERR PFX ": device file closed unexpectedly. "
- "Will not stop the WDT!\n");
+ pr_err("device file closed unexpectedly. Will not stop the WDT!\n");
}
clear_bit(0, &zf_is_open);
zf_expect_close = 0;
@@ -390,19 +390,18 @@ static void __init zf_show_action(int act)
{
static const char * const str[] = { "RESET", "SMI", "NMI", "SCI" };
- printk(KERN_INFO PFX ": Watchdog using action = %s\n", str[act]);
+ pr_info("Watchdog using action = %s\n", str[act]);
}
static int __init zf_init(void)
{
int ret;
- printk(KERN_INFO PFX
- ": MachZ ZF-Logic Watchdog driver initializing.\n");
+ pr_info("MachZ ZF-Logic Watchdog driver initializing\n");
ret = zf_get_ZFL_version();
if (!ret || ret == 0xffff) {
- printk(KERN_WARNING PFX ": no ZF-Logic found\n");
+ pr_warn("no ZF-Logic found\n");
return -ENODEV;
}
@@ -414,23 +413,20 @@ static int __init zf_init(void)
zf_show_action(action);
if (!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")) {
- printk(KERN_ERR "cannot reserve I/O ports at %d\n",
- ZF_IOBASE);
+ pr_err("cannot reserve I/O ports at %d\n", ZF_IOBASE);
ret = -EBUSY;
goto no_region;
}
ret = register_reboot_notifier(&zf_notifier);
if (ret) {
- printk(KERN_ERR "can't register reboot notifier (err=%d)\n",
- ret);
+ pr_err("can't register reboot notifier (err=%d)\n", ret);
goto no_reboot;
}
ret = misc_register(&zf_miscdev);
if (ret) {
- printk(KERN_ERR "can't misc_register on minor=%d\n",
- WATCHDOG_MINOR);
+ pr_err("can't misc_register on minor=%d\n", WATCHDOG_MINOR);
goto no_misc;
}
diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c
index af63ecf..8f4a74e 100644
--- a/drivers/watchdog/max63xx_wdt.c
+++ b/drivers/watchdog/max63xx_wdt.c
@@ -18,23 +18,20 @@
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
-#include <linux/uaccess.h>
#include <linux/io.h>
-#include <linux/device.h>
#include <linux/slab.h>
#define DEFAULT_HEARTBEAT 60
#define MAX_HEARTBEAT 60
-static int heartbeat = DEFAULT_HEARTBEAT;
-static int nowayout = WATCHDOG_NOWAYOUT;
+static unsigned int heartbeat = DEFAULT_HEARTBEAT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
/*
* Memory mapping: a single byte, 3 first lower bits to select bit 3
@@ -45,15 +42,8 @@ static int nowayout = WATCHDOG_NOWAYOUT;
static DEFINE_SPINLOCK(io_lock);
-static unsigned long wdt_status;
-#define WDT_IN_USE 0
-#define WDT_RUNNING 1
-#define WDT_OK_TO_CLOSE 2
-
static int nodelay;
-static struct resource *wdt_mem;
static void __iomem *wdt_base;
-static struct platform_device *max63xx_pdev;
/*
* The timeout values used are actually the absolute minimum the chip
@@ -117,7 +107,7 @@ max63xx_select_timeout(struct max63xx_timeout *table, int value)
return NULL;
}
-static void max63xx_wdt_ping(void)
+static int max63xx_wdt_ping(struct watchdog_device *wdd)
{
u8 val;
@@ -129,15 +119,14 @@ static void max63xx_wdt_ping(void)
__raw_writeb(val & ~MAX6369_WDI, wdt_base);
spin_unlock(&io_lock);
+ return 0;
}
-static void max63xx_wdt_enable(struct max63xx_timeout *entry)
+static int max63xx_wdt_start(struct watchdog_device *wdd)
{
+ struct max63xx_timeout *entry = watchdog_get_drvdata(wdd);
u8 val;
- if (test_and_set_bit(WDT_RUNNING, &wdt_status))
- return;
-
spin_lock(&io_lock);
val = __raw_readb(wdt_base);
@@ -149,10 +138,11 @@ static void max63xx_wdt_enable(struct max63xx_timeout *entry)
/* check for a edge triggered startup */
if (entry->tdelay == 0)
- max63xx_wdt_ping();
+ max63xx_wdt_ping(wdd);
+ return 0;
}
-static void max63xx_wdt_disable(void)
+static int max63xx_wdt_stop(struct watchdog_device *wdd)
{
u8 val;
@@ -164,113 +154,29 @@ static void max63xx_wdt_disable(void)
__raw_writeb(val, wdt_base);
spin_unlock(&io_lock);
-
- clear_bit(WDT_RUNNING, &wdt_status);
-}
-
-static int max63xx_wdt_open(struct inode *inode, struct file *file)
-{
- if (test_and_set_bit(WDT_IN_USE, &wdt_status))
- return -EBUSY;
-
- max63xx_wdt_enable(current_timeout);
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
- return nonseekable_open(inode, file);
-}
-
-static ssize_t max63xx_wdt_write(struct file *file, const char *data,
- size_t len, loff_t *ppos)
-{
- if (len) {
- if (!nowayout) {
- size_t i;
-
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
- for (i = 0; i != len; i++) {
- char c;
-
- if (get_user(c, data + i))
- return -EFAULT;
-
- if (c == 'V')
- set_bit(WDT_OK_TO_CLOSE, &wdt_status);
- }
- }
-
- max63xx_wdt_ping();
- }
-
- return len;
+ return 0;
}
-static const struct watchdog_info ident = {
- .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
+static const struct watchdog_info max63xx_wdt_info = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "max63xx Watchdog",
};
-static long max63xx_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int ret = -ENOTTY;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = copy_to_user((struct watchdog_info *)arg, &ident,
- sizeof(ident)) ? -EFAULT : 0;
- break;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- ret = put_user(0, (int *)arg);
- break;
-
- case WDIOC_KEEPALIVE:
- max63xx_wdt_ping();
- ret = 0;
- break;
-
- case WDIOC_GETTIMEOUT:
- ret = put_user(heartbeat, (int *)arg);
- break;
- }
- return ret;
-}
-
-static int max63xx_wdt_release(struct inode *inode, struct file *file)
-{
- if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
- max63xx_wdt_disable();
- else
- dev_crit(&max63xx_pdev->dev,
- "device closed unexpectedly - timer will not stop\n");
-
- clear_bit(WDT_IN_USE, &wdt_status);
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
- return 0;
-}
-
-static const struct file_operations max63xx_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = max63xx_wdt_write,
- .unlocked_ioctl = max63xx_wdt_ioctl,
- .open = max63xx_wdt_open,
- .release = max63xx_wdt_release,
+static const struct watchdog_ops max63xx_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = max63xx_wdt_start,
+ .stop = max63xx_wdt_stop,
+ .ping = max63xx_wdt_ping,
};
-static struct miscdevice max63xx_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &max63xx_wdt_fops,
+static struct watchdog_device max63xx_wdt_dev = {
+ .info = &max63xx_wdt_info,
+ .ops = &max63xx_wdt_ops,
};
static int __devinit max63xx_wdt_probe(struct platform_device *pdev)
{
- int ret = 0;
- int size;
- struct device *dev = &pdev->dev;
+ struct resource *wdt_mem;
struct max63xx_timeout *table;
table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
@@ -278,68 +184,34 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev)
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
heartbeat = DEFAULT_HEARTBEAT;
- dev_info(dev, "requesting %ds heartbeat\n", heartbeat);
+ dev_info(&pdev->dev, "requesting %ds heartbeat\n", heartbeat);
current_timeout = max63xx_select_timeout(table, heartbeat);
if (!current_timeout) {
- dev_err(dev, "unable to satisfy heartbeat request\n");
+ dev_err(&pdev->dev, "unable to satisfy heartbeat request\n");
return -EINVAL;
}
- dev_info(dev, "using %ds heartbeat with %ds initial delay\n",
+ dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay\n",
current_timeout->twd, current_timeout->tdelay);
heartbeat = current_timeout->twd;
- max63xx_pdev = pdev;
-
wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (wdt_mem == NULL) {
- dev_err(dev, "failed to get memory region resource\n");
- return -ENOENT;
- }
+ wdt_base = devm_request_and_ioremap(&pdev->dev, wdt_mem);
+ if (!wdt_base)
+ return -ENOMEM;
- size = resource_size(wdt_mem);
- if (!request_mem_region(wdt_mem->start, size, pdev->name)) {
- dev_err(dev, "failed to get memory region\n");
- return -ENOENT;
- }
-
- wdt_base = ioremap(wdt_mem->start, size);
- if (!wdt_base) {
- dev_err(dev, "failed to map memory region\n");
- ret = -ENOMEM;
- goto out_request;
- }
+ max63xx_wdt_dev.timeout = heartbeat;
+ watchdog_set_nowayout(&max63xx_wdt_dev, nowayout);
+ watchdog_set_drvdata(&max63xx_wdt_dev, current_timeout);
- ret = misc_register(&max63xx_wdt_miscdev);
- if (ret < 0) {
- dev_err(dev, "cannot register misc device\n");
- goto out_unmap;
- }
-
- return 0;
-
-out_unmap:
- iounmap(wdt_base);
-out_request:
- release_mem_region(wdt_mem->start, size);
- wdt_mem = NULL;
-
- return ret;
+ return watchdog_register_device(&max63xx_wdt_dev);
}
static int __devexit max63xx_wdt_remove(struct platform_device *pdev)
{
- misc_deregister(&max63xx_wdt_miscdev);
- if (wdt_mem) {
- release_mem_region(wdt_mem->start, resource_size(wdt_mem));
- wdt_mem = NULL;
- }
-
- if (wdt_base)
- iounmap(wdt_base);
-
+ watchdog_unregister_device(&max63xx_wdt_dev);
return 0;
}
@@ -375,7 +247,7 @@ MODULE_PARM_DESC(heartbeat,
__MODULE_STRING(MAX_HEARTBEAT) ", default "
__MODULE_STRING(DEFAULT_HEARTBEAT));
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/mixcomwd.c b/drivers/watchdog/mixcomwd.c
index bc820d1..37e4b52 100644
--- a/drivers/watchdog/mixcomwd.c
+++ b/drivers/watchdog/mixcomwd.c
@@ -39,9 +39,10 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define VERSION "0.6"
#define WATCHDOG_NAME "mixcomwd"
-#define PFX WATCHDOG_NAME ": "
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -107,8 +108,8 @@ static int mixcomwd_timer_alive;
static DEFINE_TIMER(mixcomwd_timer, mixcomwd_timerfun, 0, 0);
static char expect_close;
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -156,15 +157,13 @@ static int mixcomwd_release(struct inode *inode, struct file *file)
{
if (expect_close == 42) {
if (mixcomwd_timer_alive) {
- printk(KERN_ERR PFX
- "release called while internal timer alive");
+ pr_err("release called while internal timer alive\n");
return -EBUSY;
}
mixcomwd_timer_alive = 1;
mod_timer(&mixcomwd_timer, jiffies + 5 * HZ);
} else
- printk(KERN_CRIT PFX
- "WDT device closed unexpectedly. WDT will not stop!\n");
+ pr_crit("WDT device closed unexpectedly. WDT will not stop!\n");
clear_bit(0, &mixcomwd_opened);
expect_close = 0;
@@ -274,22 +273,19 @@ static int __init mixcomwd_init(void)
}
if (!found) {
- printk(KERN_ERR PFX
- "No card detected, or port not available.\n");
+ pr_err("No card detected, or port not available\n");
return -ENODEV;
}
ret = misc_register(&mixcomwd_miscdev);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto error_misc_register_watchdog;
}
- printk(KERN_INFO
- "MixCOM watchdog driver v%s, watchdog port at 0x%3x\n",
- VERSION, watchdog_port);
+ pr_info("MixCOM watchdog driver v%s, watchdog port at 0x%3x\n",
+ VERSION, watchdog_port);
return 0;
@@ -303,8 +299,7 @@ static void __exit mixcomwd_exit(void)
{
if (!nowayout) {
if (mixcomwd_timer_alive) {
- printk(KERN_WARNING PFX "I quit now, hardware will"
- " probably reboot!\n");
+ pr_warn("I quit now, hardware will probably reboot!\n");
del_timer_sync(&mixcomwd_timer);
mixcomwd_timer_alive = 0;
}
diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c
index 20feb4d..40f7bf1 100644
--- a/drivers/watchdog/mpc8xxx_wdt.c
+++ b/drivers/watchdog/mpc8xxx_wdt.c
@@ -17,6 +17,8 @@
* option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -60,8 +62,8 @@ module_param(reset, bool, 0);
MODULE_PARM_DESC(reset,
"Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -96,7 +98,7 @@ static void mpc8xxx_wdt_timer_ping(unsigned long arg)
static void mpc8xxx_wdt_pr_warn(const char *msg)
{
- pr_crit("mpc8xxx_wdt: %s, expect the %s soon!\n", msg,
+ pr_crit("%s, expect the %s soon!\n", msg,
reset ? "reset" : "machine check exception");
}
@@ -209,7 +211,7 @@ static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev)
enabled = in_be32(&wd_base->swcrr) & SWCRR_SWEN;
if (!enabled && wdt_type->hw_enabled) {
- pr_info("mpc8xxx_wdt: could not be enabled in software\n");
+ pr_info("could not be enabled in software\n");
ret = -ENOSYS;
goto err_unmap;
}
@@ -226,9 +228,8 @@ static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev)
goto err_unmap;
#endif
- pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d "
- "(%d seconds)\n", reset ? "reset" : "interrupt", timeout,
- timeout_sec);
+ pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d (%d seconds)\n",
+ reset ? "reset" : "interrupt", timeout, timeout_sec);
/*
* If the watchdog was previously enabled or we're running on
@@ -303,7 +304,7 @@ static int mpc8xxx_wdt_init_late(void)
ret = misc_register(&mpc8xxx_wdt_miscdev);
if (ret) {
pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ WATCHDOG_MINOR, ret);
return ret;
}
return 0;
diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c
index 82ccd36..7c741dc 100644
--- a/drivers/watchdog/mpcore_wdt.c
+++ b/drivers/watchdog/mpcore_wdt.c
@@ -19,6 +19,9 @@
* (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
*
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -44,7 +47,7 @@ struct mpcore_wdt {
char expect_close;
};
-static struct platform_device *mpcore_wdt_dev;
+static struct platform_device *mpcore_wdt_pdev;
static DEFINE_SPINLOCK(wdt_lock);
#define TIMER_MARGIN 60
@@ -54,8 +57,8 @@ MODULE_PARM_DESC(mpcore_margin,
"MPcore timer margin in seconds. (0 < mpcore_margin < 65536, default="
__MODULE_STRING(TIMER_MARGIN) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -148,7 +151,7 @@ static int mpcore_wdt_set_heartbeat(int t)
*/
static int mpcore_wdt_open(struct inode *inode, struct file *file)
{
- struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_dev);
+ struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_pdev);
if (test_and_set_bit(0, &wdt->timer_alive))
return -EBUSY;
@@ -298,9 +301,9 @@ static long mpcore_wdt_ioctl(struct file *file, unsigned int cmd,
* System shutdown handler. Turn off the watchdog if we're
* restarting or halting the system.
*/
-static void mpcore_wdt_shutdown(struct platform_device *dev)
+static void mpcore_wdt_shutdown(struct platform_device *pdev)
{
- struct mpcore_wdt *wdt = platform_get_drvdata(dev);
+ struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT)
mpcore_wdt_stop(wdt);
@@ -324,99 +327,79 @@ static struct miscdevice mpcore_wdt_miscdev = {
.fops = &mpcore_wdt_fops,
};
-static int __devinit mpcore_wdt_probe(struct platform_device *dev)
+static int __devinit mpcore_wdt_probe(struct platform_device *pdev)
{
struct mpcore_wdt *wdt;
struct resource *res;
int ret;
/* We only accept one device, and it must have an id of -1 */
- if (dev->id != -1)
+ if (pdev->id != -1)
return -ENODEV;
- res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENODEV;
- goto err_out;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
- wdt = kzalloc(sizeof(struct mpcore_wdt), GFP_KERNEL);
- if (!wdt) {
- ret = -ENOMEM;
- goto err_out;
+ wdt = devm_kzalloc(&pdev->dev, sizeof(struct mpcore_wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->dev = &pdev->dev;
+ wdt->irq = platform_get_irq(pdev, 0);
+ if (wdt->irq >= 0) {
+ ret = devm_request_irq(wdt->dev, wdt->irq, mpcore_wdt_fire, 0,
+ "mpcore_wdt", wdt);
+ if (ret) {
+ dev_printk(KERN_ERR, wdt->dev,
+ "cannot register IRQ%d for watchdog\n",
+ wdt->irq);
+ return ret;
+ }
}
- wdt->dev = &dev->dev;
- wdt->irq = platform_get_irq(dev, 0);
- if (wdt->irq < 0) {
- ret = -ENXIO;
- goto err_free;
- }
- wdt->base = ioremap(res->start, resource_size(res));
- if (!wdt->base) {
- ret = -ENOMEM;
- goto err_free;
- }
+ wdt->base = devm_ioremap(wdt->dev, res->start, resource_size(res));
+ if (!wdt->base)
+ return -ENOMEM;
- mpcore_wdt_miscdev.parent = &dev->dev;
+ mpcore_wdt_miscdev.parent = &pdev->dev;
ret = misc_register(&mpcore_wdt_miscdev);
if (ret) {
dev_printk(KERN_ERR, wdt->dev,
"cannot register miscdev on minor=%d (err=%d)\n",
WATCHDOG_MINOR, ret);
- goto err_misc;
- }
-
- ret = request_irq(wdt->irq, mpcore_wdt_fire, 0, "mpcore_wdt", wdt);
- if (ret) {
- dev_printk(KERN_ERR, wdt->dev,
- "cannot register IRQ%d for watchdog\n", wdt->irq);
- goto err_irq;
+ return ret;
}
mpcore_wdt_stop(wdt);
- platform_set_drvdata(dev, wdt);
- mpcore_wdt_dev = dev;
+ platform_set_drvdata(pdev, wdt);
+ mpcore_wdt_pdev = pdev;
return 0;
-
-err_irq:
- misc_deregister(&mpcore_wdt_miscdev);
-err_misc:
- iounmap(wdt->base);
-err_free:
- kfree(wdt);
-err_out:
- return ret;
}
-static int __devexit mpcore_wdt_remove(struct platform_device *dev)
+static int __devexit mpcore_wdt_remove(struct platform_device *pdev)
{
- struct mpcore_wdt *wdt = platform_get_drvdata(dev);
-
- platform_set_drvdata(dev, NULL);
+ platform_set_drvdata(pdev, NULL);
misc_deregister(&mpcore_wdt_miscdev);
- mpcore_wdt_dev = NULL;
+ mpcore_wdt_pdev = NULL;
- free_irq(wdt->irq, wdt);
- iounmap(wdt->base);
- kfree(wdt);
return 0;
}
#ifdef CONFIG_PM
-static int mpcore_wdt_suspend(struct platform_device *dev, pm_message_t msg)
+static int mpcore_wdt_suspend(struct platform_device *pdev, pm_message_t msg)
{
- struct mpcore_wdt *wdt = platform_get_drvdata(dev);
+ struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
mpcore_wdt_stop(wdt); /* Turn the WDT off */
return 0;
}
-static int mpcore_wdt_resume(struct platform_device *dev)
+static int mpcore_wdt_resume(struct platform_device *pdev)
{
- struct mpcore_wdt *wdt = platform_get_drvdata(dev);
+ struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
/* re-activate timer */
if (test_bit(0, &wdt->timer_alive))
mpcore_wdt_start(wdt);
@@ -442,9 +425,6 @@ static struct platform_driver mpcore_wdt_driver = {
},
};
-static char banner[] __initdata = KERN_INFO "MPcore Watchdog Timer: 0.1. "
- "mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n";
-
static int __init mpcore_wdt_init(void)
{
/*
@@ -453,11 +433,12 @@ static int __init mpcore_wdt_init(void)
*/
if (mpcore_wdt_set_heartbeat(mpcore_margin)) {
mpcore_wdt_set_heartbeat(TIMER_MARGIN);
- printk(KERN_INFO "mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
+ pr_info("mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
TIMER_MARGIN);
}
- printk(banner, mpcore_noboot, mpcore_margin, nowayout);
+ pr_info("MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n",
+ mpcore_noboot, mpcore_margin, nowayout);
return platform_driver_register(&mpcore_wdt_driver);
}
diff --git a/drivers/watchdog/mv64x60_wdt.c b/drivers/watchdog/mv64x60_wdt.c
index 97f8a48..c53d025 100644
--- a/drivers/watchdog/mv64x60_wdt.c
+++ b/drivers/watchdog/mv64x60_wdt.c
@@ -15,6 +15,8 @@
* or implied.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -58,8 +60,8 @@ static unsigned int bus_clk;
static char expect_close;
static DEFINE_SPINLOCK(mv64x60_wdt_spinlock);
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -100,7 +102,7 @@ static void mv64x60_wdt_handler_enable(void)
if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_FALSE,
MV64x60_WDC_ENABLE_SHIFT)) {
mv64x60_wdt_service();
- printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n");
+ pr_notice("watchdog activated\n");
}
}
@@ -108,7 +110,7 @@ static void mv64x60_wdt_handler_disable(void)
{
if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE,
MV64x60_WDC_ENABLE_SHIFT))
- printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n");
+ pr_notice("watchdog deactivated\n");
}
static void mv64x60_wdt_set_timeout(unsigned int timeout)
@@ -139,8 +141,7 @@ static int mv64x60_wdt_release(struct inode *inode, struct file *file)
if (expect_close == 42)
mv64x60_wdt_handler_disable();
else {
- printk(KERN_CRIT
- "mv64x60_wdt: unexpected close, not stopping timer!\n");
+ pr_crit("unexpected close, not stopping timer!\n");
mv64x60_wdt_service();
}
expect_close = 0;
@@ -308,7 +309,7 @@ static struct platform_driver mv64x60_wdt_driver = {
static int __init mv64x60_wdt_init(void)
{
- printk(KERN_INFO "MV64x60 watchdog driver\n");
+ pr_info("MV64x60 watchdog driver\n");
return platform_driver_register(&mv64x60_wdt_driver);
}
diff --git a/drivers/watchdog/nuc900_wdt.c b/drivers/watchdog/nuc900_wdt.c
index 529085b..ea4c744 100644
--- a/drivers/watchdog/nuc900_wdt.c
+++ b/drivers/watchdog/nuc900_wdt.c
@@ -55,8 +55,8 @@ module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c
index 809f41c..6bbb9ef 100644
--- a/drivers/watchdog/nv_tco.c
+++ b/drivers/watchdog/nv_tco.c
@@ -21,6 +21,8 @@
* Includes, defines, variables, module parameters, ...
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -41,7 +43,6 @@
#define TCO_VERSION "0.01"
#define TCO_MODULE_NAME "NV_TCO"
#define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION
-#define PFX TCO_MODULE_NAME ": "
/* internal variables */
static unsigned int tcobase;
@@ -60,8 +61,8 @@ module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<heartbeat<39, "
"default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"
" (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -169,8 +170,7 @@ static int nv_tco_release(struct inode *inode, struct file *file)
if (tco_expect_close == 42) {
tco_timer_stop();
} else {
- printk(KERN_CRIT PFX "Unexpected close, not stopping "
- "watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
tco_timer_keepalive();
}
clear_bit(0, &timer_alive);
@@ -323,15 +323,14 @@ static unsigned char __devinit nv_tco_getdevice(void)
val &= 0xffff;
if (val == 0x0001 || val == 0x0000) {
/* Something is wrong here, bar isn't setup */
- printk(KERN_ERR PFX "failed to get tcobase address\n");
+ pr_err("failed to get tcobase address\n");
return 0;
}
val &= 0xff00;
tcobase = val + 0x40;
if (!request_region(tcobase, 0x10, "NV TCO")) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
- tcobase);
+ pr_err("I/O address 0x%04x already in use\n", tcobase);
return 0;
}
@@ -347,7 +346,7 @@ static unsigned char __devinit nv_tco_getdevice(void)
/* Disable SMI caused by TCO */
if (!request_region(MCP51_SMI_EN(tcobase), 4, "NV TCO")) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ pr_err("I/O address 0x%04x already in use\n",
MCP51_SMI_EN(tcobase));
goto out;
}
@@ -357,7 +356,7 @@ static unsigned char __devinit nv_tco_getdevice(void)
val = inl(MCP51_SMI_EN(tcobase));
release_region(MCP51_SMI_EN(tcobase), 4);
if (val & MCP51_SMI_EN_TCO) {
- printk(KERN_ERR PFX "Could not disable SMI caused by TCO\n");
+ pr_err("Could not disable SMI caused by TCO\n");
goto out;
}
@@ -367,8 +366,7 @@ static unsigned char __devinit nv_tco_getdevice(void)
pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);
pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
if (!(val & MCP51_SMBUS_SETUP_B_TCO_REBOOT)) {
- printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot "
- "disabled by hardware\n");
+ pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware\n");
goto out;
}
@@ -387,8 +385,8 @@ static int __devinit nv_tco_init(struct platform_device *dev)
return -ENODEV;
/* Check to see if last reboot was due to watchdog timeout */
- printk(KERN_INFO PFX "Watchdog reboot %sdetected.\n",
- inl(TCO_STS(tcobase)) & TCO_STS_TCO2TO_STS ? "" : "not ");
+ pr_info("Watchdog reboot %sdetected\n",
+ inl(TCO_STS(tcobase)) & TCO_STS_TCO2TO_STS ? "" : "not ");
/* Clear out the old status */
outl(TCO_STS_RESET, TCO_STS(tcobase));
@@ -400,14 +398,14 @@ static int __devinit nv_tco_init(struct platform_device *dev)
if (tco_timer_set_heartbeat(heartbeat)) {
heartbeat = WATCHDOG_HEARTBEAT;
tco_timer_set_heartbeat(heartbeat);
- printk(KERN_INFO PFX "heartbeat value must be 2<heartbeat<39, "
- "using %d\n", heartbeat);
+ pr_info("heartbeat value must be 2<heartbeat<39, using %d\n",
+ heartbeat);
}
ret = misc_register(&nv_tco_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX "cannot register miscdev on minor=%d "
- "(err=%d)\n", WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto unreg_region;
}
@@ -415,8 +413,8 @@ static int __devinit nv_tco_init(struct platform_device *dev)
tco_timer_stop();
- printk(KERN_INFO PFX "initialized (0x%04x). heartbeat=%d sec "
- "(nowayout=%d)\n", tcobase, heartbeat, nowayout);
+ pr_info("initialized (0x%04x). heartbeat=%d sec (nowayout=%d)\n",
+ tcobase, heartbeat, nowayout);
return 0;
@@ -439,8 +437,7 @@ static void __devexit nv_tco_cleanup(void)
pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);
pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
if (val & MCP51_SMBUS_SETUP_B_TCO_REBOOT) {
- printk(KERN_CRIT PFX "Couldn't unset REBOOT bit. Machine may "
- "soon reset\n");
+ pr_crit("Couldn't unset REBOOT bit. Machine may soon reset\n");
}
/* Deregister */
@@ -483,8 +480,7 @@ static int __init nv_tco_init_module(void)
{
int err;
- printk(KERN_INFO PFX "NV TCO WatchDog Timer Driver v%s\n",
- TCO_VERSION);
+ pr_info("NV TCO WatchDog Timer Driver v%s\n", TCO_VERSION);
err = platform_driver_register(&nv_tco_driver);
if (err)
@@ -508,7 +504,7 @@ static void __exit nv_tco_cleanup_module(void)
{
platform_device_unregister(nv_tco_platform_device);
platform_driver_unregister(&nv_tco_driver);
- printk(KERN_INFO PFX "NV TCO Watchdog Module Unloaded.\n");
+ pr_info("NV TCO Watchdog Module Unloaded\n");
}
module_init(nv_tco_init_module);
diff --git a/drivers/watchdog/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c
index 7c0d863..4612088 100644
--- a/drivers/watchdog/octeon-wdt-main.c
+++ b/drivers/watchdog/octeon-wdt-main.c
@@ -52,6 +52,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/watchdog.h>
@@ -95,8 +97,8 @@ MODULE_PARM_DESC(heartbeat,
"Watchdog heartbeat in seconds. (0 < heartbeat, default="
__MODULE_STRING(WD_TIMO) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, S_IRUGO);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, S_IRUGO);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -201,7 +203,7 @@ static void __init octeon_wdt_build_stage1(void)
uasm_resolve_relocs(relocs, labels);
len = (int)(p - nmi_stage1_insns);
- pr_debug("Synthesized NMI stage 1 handler (%d instructions).\n", len);
+ pr_debug("Synthesized NMI stage 1 handler (%d instructions)\n", len);
pr_debug("\t.set push\n");
pr_debug("\t.set noreorder\n");
@@ -627,7 +629,7 @@ static int octeon_wdt_release(struct inode *inode, struct file *file)
do_coundown = 0;
octeon_wdt_ping();
} else {
- pr_crit("octeon_wdt: WDT device closed unexpectedly. WDT will not stop!\n");
+ pr_crit("WDT device closed unexpectedly. WDT will not stop!\n");
}
clear_bit(0, &octeon_wdt_is_open);
expect_close = 0;
@@ -684,12 +686,12 @@ static int __init octeon_wdt_init(void)
octeon_wdt_calc_parameters(heartbeat);
- pr_info("octeon_wdt: Initial granularity %d Sec.\n", timeout_sec);
+ pr_info("Initial granularity %d Sec\n", timeout_sec);
ret = misc_register(&octeon_wdt_miscdev);
if (ret) {
- pr_err("octeon_wdt: cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto out;
}
diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c
index f359ab8..55d2f66 100644
--- a/drivers/watchdog/of_xilinx_wdt.c
+++ b/drivers/watchdog/of_xilinx_wdt.c
@@ -19,6 +19,8 @@
* know the wdt reset interval
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
@@ -99,7 +101,7 @@ static void xwdt_stop(void)
iowrite32(0, xdev.base + XWT_TWCSR1_OFFSET);
spin_unlock(&spinlock);
- printk(KERN_INFO PFX "Stopped!\n");
+ pr_info("Stopped!\n");
}
static void xwdt_keepalive(void)
@@ -165,7 +167,7 @@ static int xwdt_open(struct inode *inode, struct file *file)
__module_get(THIS_MODULE);
xwdt_start();
- printk(KERN_INFO PFX "Started...\n");
+ pr_info("Started...\n");
return nonseekable_open(inode, file);
}
@@ -175,8 +177,7 @@ static int xwdt_release(struct inode *inode, struct file *file)
if (expect_close == 42) {
xwdt_stop();
} else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
xwdt_keepalive();
}
@@ -300,22 +301,20 @@ static int __devinit xwdt_probe(struct platform_device *pdev)
"clock-frequency", NULL);
if (pfreq == NULL) {
- printk(KERN_WARNING PFX
- "The watchdog clock frequency cannot be obtained!\n");
+ pr_warn("The watchdog clock frequency cannot be obtained!\n");
no_timeout = 1;
}
rc = of_address_to_resource(pdev->dev.of_node, 0, &xdev.res);
if (rc) {
- printk(KERN_WARNING PFX "invalid address!\n");
+ pr_warn("invalid address!\n");
return rc;
}
tmptr = (u32 *)of_get_property(pdev->dev.of_node,
"xlnx,wdt-interval", NULL);
if (tmptr == NULL) {
- printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-interval\""
- " not found in device tree!\n");
+ pr_warn("Parameter \"xlnx,wdt-interval\" not found in device tree!\n");
no_timeout = 1;
} else {
xdev.wdt_interval = *tmptr;
@@ -324,8 +323,7 @@ static int __devinit xwdt_probe(struct platform_device *pdev)
tmptr = (u32 *)of_get_property(pdev->dev.of_node,
"xlnx,wdt-enable-once", NULL);
if (tmptr == NULL) {
- printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-enable-once\""
- " not found in device tree!\n");
+ pr_warn("Parameter \"xlnx,wdt-enable-once\" not found in device tree!\n");
xdev.nowayout = WATCHDOG_NOWAYOUT;
}
@@ -339,20 +337,20 @@ static int __devinit xwdt_probe(struct platform_device *pdev)
if (!request_mem_region(xdev.res.start,
xdev.res.end - xdev.res.start + 1, WATCHDOG_NAME)) {
rc = -ENXIO;
- printk(KERN_ERR PFX "memory request failure!\n");
+ pr_err("memory request failure!\n");
goto err_out;
}
xdev.base = ioremap(xdev.res.start, xdev.res.end - xdev.res.start + 1);
if (xdev.base == NULL) {
rc = -ENOMEM;
- printk(KERN_ERR PFX "ioremap failure!\n");
+ pr_err("ioremap failure!\n");
goto release_mem;
}
rc = xwdt_selftest();
if (rc == XWT_TIMER_FAILED) {
- printk(KERN_ERR PFX "SelfTest routine error!\n");
+ pr_err("SelfTest routine error!\n");
goto unmap_io;
}
@@ -360,20 +358,17 @@ static int __devinit xwdt_probe(struct platform_device *pdev)
rc = misc_register(&xwdt_miscdev);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- xwdt_miscdev.minor, rc);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ xwdt_miscdev.minor, rc);
goto unmap_io;
}
if (no_timeout)
- printk(KERN_INFO PFX
- "driver loaded (timeout=? sec, nowayout=%d)\n",
- xdev.nowayout);
+ pr_info("driver loaded (timeout=? sec, nowayout=%d)\n",
+ xdev.nowayout);
else
- printk(KERN_INFO PFX
- "driver loaded (timeout=%d sec, nowayout=%d)\n",
- timeout, xdev.nowayout);
+ pr_info("driver loaded (timeout=%d sec, nowayout=%d)\n",
+ timeout, xdev.nowayout);
expect_close = 0;
clear_bit(0, &driver_open);
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index d19ff51..8285d65 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -26,6 +26,8 @@
* Use the driver model and standard identifiers; handle bigger timeouts.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
@@ -183,7 +185,7 @@ static int omap_wdt_release(struct inode *inode, struct file *file)
pm_runtime_put_sync(wdev->dev);
#else
- printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n");
+ pr_crit("Unexpected close, not stopping!\n");
#endif
wdev->omap_wdt_users = 0;
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index 4ad78f8..788aa15 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -10,6 +10,8 @@
* warranty of any kind, whether express or implied.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -28,18 +30,19 @@
/*
* Watchdog timer block registers.
*/
-#define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000)
+#define TIMER_CTRL 0x0000
#define WDT_EN 0x0010
-#define WDT_VAL (TIMER_VIRT_BASE + 0x0024)
+#define WDT_VAL 0x0024
#define WDT_MAX_CYCLE_COUNT 0xffffffff
#define WDT_IN_USE 0
#define WDT_OK_TO_CLOSE 1
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = -1; /* module parameter (seconds) */
static unsigned int wdt_max_duration; /* (seconds) */
static unsigned int wdt_tclk;
+static void __iomem *wdt_reg;
static unsigned long wdt_status;
static DEFINE_SPINLOCK(wdt_lock);
@@ -48,7 +51,7 @@ static void orion_wdt_ping(void)
spin_lock(&wdt_lock);
/* Reload watchdog duration */
- writel(wdt_tclk * heartbeat, WDT_VAL);
+ writel(wdt_tclk * heartbeat, wdt_reg + WDT_VAL);
spin_unlock(&wdt_lock);
}
@@ -60,7 +63,7 @@ static void orion_wdt_enable(void)
spin_lock(&wdt_lock);
/* Set watchdog duration */
- writel(wdt_tclk * heartbeat, WDT_VAL);
+ writel(wdt_tclk * heartbeat, wdt_reg + WDT_VAL);
/* Clear watchdog timer interrupt */
reg = readl(BRIDGE_CAUSE);
@@ -68,9 +71,9 @@ static void orion_wdt_enable(void)
writel(reg, BRIDGE_CAUSE);
/* Enable watchdog timer */
- reg = readl(TIMER_CTRL);
+ reg = readl(wdt_reg + TIMER_CTRL);
reg |= WDT_EN;
- writel(reg, TIMER_CTRL);
+ writel(reg, wdt_reg + TIMER_CTRL);
/* Enable reset on watchdog */
reg = readl(RSTOUTn_MASK);
@@ -92,9 +95,9 @@ static void orion_wdt_disable(void)
writel(reg, RSTOUTn_MASK);
/* Disable watchdog timer */
- reg = readl(TIMER_CTRL);
+ reg = readl(wdt_reg + TIMER_CTRL);
reg &= ~WDT_EN;
- writel(reg, TIMER_CTRL);
+ writel(reg, wdt_reg + TIMER_CTRL);
spin_unlock(&wdt_lock);
}
@@ -102,7 +105,7 @@ static void orion_wdt_disable(void)
static int orion_wdt_get_timeleft(int *time_left)
{
spin_lock(&wdt_lock);
- *time_left = readl(WDT_VAL) / wdt_tclk;
+ *time_left = readl(wdt_reg + WDT_VAL) / wdt_tclk;
spin_unlock(&wdt_lock);
return 0;
}
@@ -209,8 +212,7 @@ static int orion_wdt_release(struct inode *inode, struct file *file)
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
orion_wdt_disable();
else
- printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
- "timer will not stop\n");
+ pr_crit("Device closed unexpectedly - timer will not stop\n");
clear_bit(WDT_IN_USE, &wdt_status);
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
@@ -236,15 +238,20 @@ static struct miscdevice orion_wdt_miscdev = {
static int __devinit orion_wdt_probe(struct platform_device *pdev)
{
struct orion_wdt_platform_data *pdata = pdev->dev.platform_data;
+ struct resource *res;
int ret;
if (pdata) {
wdt_tclk = pdata->tclk;
} else {
- printk(KERN_ERR "Orion Watchdog misses platform data\n");
+ pr_err("misses platform data\n");
return -ENODEV;
}
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ wdt_reg = ioremap(res->start, resource_size(res));
+
if (orion_wdt_miscdev.parent)
return -EBUSY;
orion_wdt_miscdev.parent = &pdev->dev;
@@ -257,8 +264,8 @@ static int __devinit orion_wdt_probe(struct platform_device *pdev)
if (ret)
return ret;
- printk(KERN_INFO "Orion Watchdog Timer: Initial timeout %d sec%s\n",
- heartbeat, nowayout ? ", nowayout" : "");
+ pr_info("Initial timeout %d sec%s\n",
+ heartbeat, nowayout ? ", nowayout" : "");
return 0;
}
@@ -302,7 +309,7 @@ MODULE_DESCRIPTION("Orion Processor Watchdog");
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c
index e78d899..5afb89b 100644
--- a/drivers/watchdog/pc87413_wdt.c
+++ b/drivers/watchdog/pc87413_wdt.c
@@ -18,6 +18,8 @@
* Release 1.1
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
@@ -33,7 +35,6 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
/* #define DEBUG 1 */
@@ -42,7 +43,6 @@
#define VERSION "1.1"
#define MODNAME "pc87413 WDT"
-#define PFX MODNAME ": "
#define DPFX MODNAME " - DEBUG: "
#define WDT_INDEX_IO_PORT (io+0) /* I/O port base (index register) */
@@ -65,7 +65,7 @@ static char expect_close; /* is the close expected? */
static DEFINE_SPINLOCK(io_lock); /* to guard us from io races */
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
/* -- Low level function ----------------------------------------*/
@@ -87,7 +87,7 @@ static inline void pc87413_select_wdt_out(void)
outb_p(cr_data, WDT_DATA_IO_PORT);
#ifdef DEBUG
- printk(KERN_INFO DPFX
+ pr_info(DPFX
"Select multiple pin,pin55,as WDT output: Bit7 to 1: %d\n",
cr_data);
#endif
@@ -111,7 +111,7 @@ static inline void pc87413_enable_swc(void)
outb_p(cr_data, WDT_DATA_IO_PORT); /* Index0x30_bit0P1 */
#ifdef DEBUG
- printk(KERN_INFO DPFX "pc87413 - Enable SWC functions\n");
+ pr_info(DPFX "pc87413 - Enable SWC functions\n");
#endif
}
@@ -132,7 +132,7 @@ static void pc87413_get_swc_base_addr(void)
swc_base_addr = (addr_h << 8) + addr_l;
#ifdef DEBUG
- printk(KERN_INFO DPFX
+ pr_info(DPFX
"Read SWC I/O Base Address: low %d, high %d, res %d\n",
addr_l, addr_h, swc_base_addr);
#endif
@@ -145,7 +145,7 @@ static inline void pc87413_swc_bank3(void)
/* Step 4: Select Bank3 of SWC */
outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f);
#ifdef DEBUG
- printk(KERN_INFO DPFX "Select Bank3 of SWC\n");
+ pr_info(DPFX "Select Bank3 of SWC\n");
#endif
}
@@ -156,7 +156,7 @@ static inline void pc87413_programm_wdto(char pc87413_time)
/* Step 5: Programm WDTO, Twd. */
outb_p(pc87413_time, swc_base_addr + WDTO);
#ifdef DEBUG
- printk(KERN_INFO DPFX "Set WDTO to %d minutes\n", pc87413_time);
+ pr_info(DPFX "Set WDTO to %d minutes\n", pc87413_time);
#endif
}
@@ -167,7 +167,7 @@ static inline void pc87413_enable_wden(void)
/* Step 6: Enable WDEN */
outb_p(inb(swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL);
#ifdef DEBUG
- printk(KERN_INFO DPFX "Enable WDEN\n");
+ pr_info(DPFX "Enable WDEN\n");
#endif
}
@@ -177,7 +177,7 @@ static inline void pc87413_enable_sw_wd_tren(void)
/* Enable SW_WD_TREN */
outb_p(inb(swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG);
#ifdef DEBUG
- printk(KERN_INFO DPFX "Enable SW_WD_TREN\n");
+ pr_info(DPFX "Enable SW_WD_TREN\n");
#endif
}
@@ -188,7 +188,7 @@ static inline void pc87413_disable_sw_wd_tren(void)
/* Disable SW_WD_TREN */
outb_p(inb(swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG);
#ifdef DEBUG
- printk(KERN_INFO DPFX "pc87413 - Disable SW_WD_TREN\n");
+ pr_info(DPFX "pc87413 - Disable SW_WD_TREN\n");
#endif
}
@@ -199,7 +199,7 @@ static inline void pc87413_enable_sw_wd_trg(void)
/* Enable SW_WD_TRG */
outb_p(inb(swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL);
#ifdef DEBUG
- printk(KERN_INFO DPFX "pc87413 - Enable SW_WD_TRG\n");
+ pr_info(DPFX "pc87413 - Enable SW_WD_TRG\n");
#endif
}
@@ -210,7 +210,7 @@ static inline void pc87413_disable_sw_wd_trg(void)
/* Disable SW_WD_TRG */
outb_p(inb(swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL);
#ifdef DEBUG
- printk(KERN_INFO DPFX "Disable SW_WD_TRG\n");
+ pr_info(DPFX "Disable SW_WD_TRG\n");
#endif
}
@@ -284,8 +284,7 @@ static int pc87413_open(struct inode *inode, struct file *file)
/* Reload and activate timer */
pc87413_refresh();
- printk(KERN_INFO MODNAME
- "Watchdog enabled. Timeout set to %d minute(s).\n", timeout);
+ pr_info("Watchdog enabled. Timeout set to %d minute(s).\n", timeout);
return nonseekable_open(inode, file);
}
@@ -308,11 +307,9 @@ static int pc87413_release(struct inode *inode, struct file *file)
if (expect_close == 42) {
pc87413_disable();
- printk(KERN_INFO MODNAME
- "Watchdog disabled, sleeping again...\n");
+ pr_info("Watchdog disabled, sleeping again...\n");
} else {
- printk(KERN_CRIT MODNAME
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
pc87413_refresh();
}
clear_bit(0, &timer_enabled);
@@ -428,7 +425,7 @@ static long pc87413_ioctl(struct file *file, unsigned int cmd,
case WDIOC_KEEPALIVE:
pc87413_refresh();
#ifdef DEBUG
- printk(KERN_INFO DPFX "keepalive\n");
+ pr_info(DPFX "keepalive\n");
#endif
return 0;
case WDIOC_SETTIMEOUT:
@@ -508,7 +505,7 @@ static int __init pc87413_init(void)
{
int ret;
- printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n",
+ pr_info("Version " VERSION " at io 0x%X\n",
WDT_INDEX_IO_PORT);
if (!request_muxed_region(io, 2, MODNAME))
@@ -516,26 +513,23 @@ static int __init pc87413_init(void)
ret = register_reboot_notifier(&pc87413_notifier);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
}
ret = misc_register(&pc87413_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto reboot_unreg;
}
- printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout);
+ pr_info("initialized. timeout=%d min\n", timeout);
pc87413_select_wdt_out();
pc87413_enable_swc();
pc87413_get_swc_base_addr();
if (!request_region(swc_base_addr, 0x20, MODNAME)) {
- printk(KERN_ERR PFX
- "cannot request SWC region at 0x%x\n", swc_base_addr);
+ pr_err("cannot request SWC region at 0x%x\n", swc_base_addr);
ret = -EBUSY;
goto misc_unreg;
}
@@ -568,14 +562,14 @@ static void __exit pc87413_exit(void)
/* Stop the timer before we leave */
if (!nowayout) {
pc87413_disable();
- printk(KERN_INFO MODNAME "Watchdog disabled.\n");
+ pr_info("Watchdog disabled\n");
}
misc_deregister(&pc87413_miscdev);
unregister_reboot_notifier(&pc87413_notifier);
release_region(swc_base_addr, 0x20);
- printk(KERN_INFO MODNAME " watchdog component driver removed.\n");
+ pr_info("watchdog component driver removed\n");
}
module_init(pc87413_init);
@@ -597,7 +591,7 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in minutes (default="
__MODULE_STRING(DEFAULT_TIMEOUT) ").");
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c
index 06f7922..75694cf 100644
--- a/drivers/watchdog/pcwd.c
+++ b/drivers/watchdog/pcwd.c
@@ -51,6 +51,8 @@
* http://www.pcwatchdog.com/
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h> /* For module specific items */
#include <linux/moduleparam.h> /* For new moduleparam's */
#include <linux/types.h> /* For standard types (like size_t) */
@@ -75,7 +77,6 @@
#define WATCHDOG_DATE "18 Feb 2007"
#define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog"
#define WATCHDOG_NAME "pcwd"
-#define PFX WATCHDOG_NAME ": "
#define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION "\n"
/*
@@ -203,8 +204,8 @@ MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
"(2 <= heartbeat <= 7200 or 0=delay-time from dip-switches, default="
__MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -220,8 +221,7 @@ static int send_isa_command(int cmd)
int port0, last_port0; /* Double read for stabilising */
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "sending following data cmd=0x%02x\n",
- cmd);
+ pr_debug("sending following data cmd=0x%02x\n", cmd);
/* The WCMD bit must be 1 and the command is only 4 bits in size */
control_status = (cmd & 0x0F) | WD_WCMD;
@@ -240,9 +240,8 @@ static int send_isa_command(int cmd)
}
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "received following data for "
- "cmd=0x%02x: port0=0x%02x last_port0=0x%02x\n",
- cmd, port0, last_port0);
+ pr_debug("received following data for cmd=0x%02x: port0=0x%02x last_port0=0x%02x\n",
+ cmd, port0, last_port0);
return port0;
}
@@ -271,8 +270,7 @@ static int set_command_mode(void)
pcwd_private.command_mode = found;
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "command_mode=%d\n",
- pcwd_private.command_mode);
+ pr_debug("command_mode=%d\n", pcwd_private.command_mode);
return found;
}
@@ -288,8 +286,7 @@ static void unset_command_mode(void)
pcwd_private.command_mode = 0;
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "command_mode=%d\n",
- pcwd_private.command_mode);
+ pr_debug("command_mode=%d\n", pcwd_private.command_mode);
}
static inline void pcwd_check_temperature_support(void)
@@ -336,17 +333,14 @@ static void pcwd_show_card_info(void)
/* Get some extra info from the hardware (in command/debug/diag mode) */
if (pcwd_private.revision == PCWD_REVISION_A)
- printk(KERN_INFO PFX
- "ISA-PC Watchdog (REV.A) detected at port 0x%04x\n",
- pcwd_private.io_addr);
+ pr_info("ISA-PC Watchdog (REV.A) detected at port 0x%04x\n",
+ pcwd_private.io_addr);
else if (pcwd_private.revision == PCWD_REVISION_C) {
pcwd_get_firmware();
- printk(KERN_INFO PFX "ISA-PC Watchdog (REV.C) detected at port "
- "0x%04x (Firmware version: %s)\n",
+ pr_info("ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n",
pcwd_private.io_addr, pcwd_private.fw_ver_str);
option_switches = pcwd_get_option_switches();
- printk(KERN_INFO PFX "Option switches (0x%02x): "
- "Temperature Reset Enable=%s, Power On Delay=%s\n",
+ pr_info("Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
option_switches,
((option_switches & 0x10) ? "ON" : "OFF"),
((option_switches & 0x08) ? "ON" : "OFF"));
@@ -359,22 +353,18 @@ static void pcwd_show_card_info(void)
}
if (pcwd_private.supports_temp)
- printk(KERN_INFO PFX "Temperature Option Detected\n");
+ pr_info("Temperature Option Detected\n");
if (pcwd_private.boot_status & WDIOF_CARDRESET)
- printk(KERN_INFO PFX
- "Previous reboot was caused by the card\n");
+ pr_info("Previous reboot was caused by the card\n");
if (pcwd_private.boot_status & WDIOF_OVERHEAT) {
- printk(KERN_EMERG PFX
- "Card senses a CPU Overheat. Panicking!\n");
- printk(KERN_EMERG PFX
- "CPU Overheat\n");
+ pr_emerg("Card senses a CPU Overheat. Panicking!\n");
+ pr_emerg("CPU Overheat\n");
}
if (pcwd_private.boot_status == 0)
- printk(KERN_INFO PFX
- "No previous trip detected - Cold boot or reset\n");
+ pr_info("No previous trip detected - Cold boot or reset\n");
}
static void pcwd_timer_ping(unsigned long data)
@@ -404,8 +394,7 @@ static void pcwd_timer_ping(unsigned long data)
spin_unlock(&pcwd_private.io_lock);
} else {
- printk(KERN_WARNING PFX
- "Heartbeat lost! Will not ping the watchdog\n");
+ pr_warn("Heartbeat lost! Will not ping the watchdog\n");
}
}
@@ -426,13 +415,13 @@ static int pcwd_start(void)
stat_reg = inb_p(pcwd_private.io_addr + 2);
spin_unlock(&pcwd_private.io_lock);
if (stat_reg & WD_WDIS) {
- printk(KERN_INFO PFX "Could not start watchdog\n");
+ pr_info("Could not start watchdog\n");
return -EIO;
}
}
if (debug >= VERBOSE)
- printk(KERN_DEBUG PFX "Watchdog started\n");
+ pr_debug("Watchdog started\n");
return 0;
}
@@ -454,13 +443,13 @@ static int pcwd_stop(void)
stat_reg = inb_p(pcwd_private.io_addr + 2);
spin_unlock(&pcwd_private.io_lock);
if ((stat_reg & WD_WDIS) == 0) {
- printk(KERN_INFO PFX "Could not stop watchdog\n");
+ pr_info("Could not stop watchdog\n");
return -EIO;
}
}
if (debug >= VERBOSE)
- printk(KERN_DEBUG PFX "Watchdog stopped\n");
+ pr_debug("Watchdog stopped\n");
return 0;
}
@@ -471,7 +460,7 @@ static int pcwd_keepalive(void)
pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n");
+ pr_debug("Watchdog keepalive signal send\n");
return 0;
}
@@ -484,8 +473,7 @@ static int pcwd_set_heartbeat(int t)
heartbeat = t;
if (debug >= VERBOSE)
- printk(KERN_DEBUG PFX "New heartbeat: %d\n",
- heartbeat);
+ pr_debug("New heartbeat: %d\n", heartbeat);
return 0;
}
@@ -518,8 +506,7 @@ static int pcwd_get_status(int *status)
if (control_status & WD_T110) {
*status |= WDIOF_OVERHEAT;
if (temp_panic) {
- printk(KERN_INFO PFX
- "Temperature overheat trip!\n");
+ pr_info("Temperature overheat trip!\n");
kernel_power_off();
}
}
@@ -530,8 +517,7 @@ static int pcwd_get_status(int *status)
if (control_status & WD_REVC_TTRP) {
*status |= WDIOF_OVERHEAT;
if (temp_panic) {
- printk(KERN_INFO PFX
- "Temperature overheat trip!\n");
+ pr_info("Temperature overheat trip!\n");
kernel_power_off();
}
}
@@ -548,16 +534,14 @@ static int pcwd_clear_status(void)
spin_lock(&pcwd_private.io_lock);
if (debug >= VERBOSE)
- printk(KERN_INFO PFX
- "clearing watchdog trip status\n");
+ pr_info("clearing watchdog trip status\n");
control_status = inb_p(pcwd_private.io_addr + 1);
if (debug >= DEBUG) {
- printk(KERN_DEBUG PFX "status was: 0x%02x\n",
- control_status);
- printk(KERN_DEBUG PFX "sending: 0x%02x\n",
- (control_status & WD_REVC_R2DS));
+ pr_debug("status was: 0x%02x\n", control_status);
+ pr_debug("sending: 0x%02x\n",
+ (control_status & WD_REVC_R2DS));
}
/* clear reset status & Keep Relay 2 disable state as it is */
@@ -588,8 +572,7 @@ static int pcwd_get_temperature(int *temperature)
spin_unlock(&pcwd_private.io_lock);
if (debug >= DEBUG) {
- printk(KERN_DEBUG PFX "temperature is: %d F\n",
- *temperature);
+ pr_debug("temperature is: %d F\n", *temperature);
}
return 0;
@@ -720,8 +703,7 @@ static int pcwd_close(struct inode *inode, struct file *file)
if (expect_close == 42)
pcwd_stop();
else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
pcwd_keepalive();
}
expect_close = 0;
@@ -828,11 +810,10 @@ static int __devinit pcwd_isa_match(struct device *dev, unsigned int id)
int retval;
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "pcwd_isa_match id=%d\n",
- id);
+ pr_debug("pcwd_isa_match id=%d\n", id);
if (!request_region(base_addr, 4, "PCWD")) {
- printk(KERN_INFO PFX "Port 0x%04x unavailable\n", base_addr);
+ pr_info("Port 0x%04x unavailable\n", base_addr);
return 0;
}
@@ -870,21 +851,20 @@ static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id)
int ret;
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "pcwd_isa_probe id=%d\n",
- id);
+ pr_debug("pcwd_isa_probe id=%d\n", id);
cards_found++;
if (cards_found == 1)
- printk(KERN_INFO PFX "v%s Ken Hollis (kenji@bitgate.com)\n",
+ pr_info("v%s Ken Hollis (kenji@bitgate.com)\n",
WATCHDOG_VERSION);
if (cards_found > 1) {
- printk(KERN_ERR PFX "This driver only supports 1 device\n");
+ pr_err("This driver only supports 1 device\n");
return -ENODEV;
}
if (pcwd_ioports[id] == 0x0000) {
- printk(KERN_ERR PFX "No I/O-Address for card detected\n");
+ pr_err("No I/O-Address for card detected\n");
return -ENODEV;
}
pcwd_private.io_addr = pcwd_ioports[id];
@@ -896,8 +876,8 @@ static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id)
if (!request_region(pcwd_private.io_addr,
(pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
- pcwd_private.io_addr);
+ pr_err("I/O address 0x%04x already in use\n",
+ pcwd_private.io_addr);
ret = -EIO;
goto error_request_region;
}
@@ -932,30 +912,27 @@ static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id)
if not reset to the default */
if (pcwd_set_heartbeat(heartbeat)) {
pcwd_set_heartbeat(WATCHDOG_HEARTBEAT);
- printk(KERN_INFO PFX
- "heartbeat value must be 2 <= heartbeat <= 7200, using %d\n",
- WATCHDOG_HEARTBEAT);
+ pr_info("heartbeat value must be 2 <= heartbeat <= 7200, using %d\n",
+ WATCHDOG_HEARTBEAT);
}
if (pcwd_private.supports_temp) {
ret = misc_register(&temp_miscdev);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- TEMP_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ TEMP_MINOR, ret);
goto error_misc_register_temp;
}
}
ret = misc_register(&pcwd_miscdev);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto error_misc_register_watchdog;
}
- printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
+ pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
heartbeat, nowayout);
return 0;
@@ -975,8 +952,7 @@ error_request_region:
static int __devexit pcwd_isa_remove(struct device *dev, unsigned int id)
{
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "pcwd_isa_remove id=%d\n",
- id);
+ pr_debug("pcwd_isa_remove id=%d\n", id);
if (!pcwd_private.io_addr)
return 1;
@@ -1000,8 +976,7 @@ static int __devexit pcwd_isa_remove(struct device *dev, unsigned int id)
static void pcwd_isa_shutdown(struct device *dev, unsigned int id)
{
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "pcwd_isa_shutdown id=%d\n",
- id);
+ pr_debug("pcwd_isa_shutdown id=%d\n", id);
pcwd_stop();
}
@@ -1025,7 +1000,7 @@ static int __init pcwd_init_module(void)
static void __exit pcwd_cleanup_module(void)
{
isa_unregister_driver(&pcwd_isa_driver);
- printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+ pr_info("Watchdog Module Unloaded\n");
}
module_init(pcwd_init_module);
diff --git a/drivers/watchdog/pcwd_pci.c b/drivers/watchdog/pcwd_pci.c
index b8d14f8..c891399 100644
--- a/drivers/watchdog/pcwd_pci.c
+++ b/drivers/watchdog/pcwd_pci.c
@@ -32,6 +32,8 @@
* Includes, defines, variables, module parameters, ...
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h> /* For module specific items */
#include <linux/moduleparam.h> /* For new moduleparam's */
#include <linux/types.h> /* For standard types (like size_t) */
@@ -54,8 +56,7 @@
#define WATCHDOG_VERSION "1.03"
#define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog"
#define WATCHDOG_NAME "pcwd_pci"
-#define PFX WATCHDOG_NAME ": "
-#define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION "\n"
+#define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION
/* Stuff for the PCI ID's */
#ifndef PCI_VENDOR_ID_QUICKLOGIC
@@ -145,8 +146,8 @@ MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
"(0<heartbeat<65536 or 0=delay-time from dip-switches, default="
__MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -159,8 +160,8 @@ static int send_command(int cmd, int *msb, int *lsb)
int got_response, count;
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "sending following data "
- "cmd=0x%02x msb=0x%02x lsb=0x%02x\n", cmd, *msb, *lsb);
+ pr_debug("sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x\n",
+ cmd, *msb, *lsb);
spin_lock(&pcipcwd_private.io_lock);
/* If a command requires data it should be written first.
@@ -185,12 +186,10 @@ static int send_command(int cmd, int *msb, int *lsb)
if (debug >= DEBUG) {
if (got_response) {
- printk(KERN_DEBUG PFX
- "time to process command was: %d ms\n",
- count);
+ pr_debug("time to process command was: %d ms\n",
+ count);
} else {
- printk(KERN_DEBUG PFX
- "card did not respond on command!\n");
+ pr_debug("card did not respond on command!\n");
}
}
@@ -203,9 +202,8 @@ static int send_command(int cmd, int *msb, int *lsb)
inb_p(pcipcwd_private.io_addr + 6);
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "received following data for "
- "cmd=0x%02x: msb=0x%02x lsb=0x%02x\n",
- cmd, *msb, *lsb);
+ pr_debug("received following data for cmd=0x%02x: msb=0x%02x lsb=0x%02x\n",
+ cmd, *msb, *lsb);
}
spin_unlock(&pcipcwd_private.io_lock);
@@ -243,27 +241,23 @@ static void pcipcwd_show_card_info(void)
/* Get switch settings */
option_switches = pcipcwd_get_option_switches();
- printk(KERN_INFO PFX "Found card at port "
- "0x%04x (Firmware: %s) %s temp option\n",
+ pr_info("Found card at port 0x%04x (Firmware: %s) %s temp option\n",
(int) pcipcwd_private.io_addr, fw_ver_str,
(pcipcwd_private.supports_temp ? "with" : "without"));
- printk(KERN_INFO PFX "Option switches (0x%02x): "
- "Temperature Reset Enable=%s, Power On Delay=%s\n",
+ pr_info("Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
option_switches,
((option_switches & 0x10) ? "ON" : "OFF"),
((option_switches & 0x08) ? "ON" : "OFF"));
if (pcipcwd_private.boot_status & WDIOF_CARDRESET)
- printk(KERN_INFO PFX
- "Previous reset was caused by the Watchdog card\n");
+ pr_info("Previous reset was caused by the Watchdog card\n");
if (pcipcwd_private.boot_status & WDIOF_OVERHEAT)
- printk(KERN_INFO PFX "Card sensed a CPU Overheat\n");
+ pr_info("Card sensed a CPU Overheat\n");
if (pcipcwd_private.boot_status == 0)
- printk(KERN_INFO PFX
- "No previous trip detected - Cold boot or reset\n");
+ pr_info("No previous trip detected - Cold boot or reset\n");
}
static int pcipcwd_start(void)
@@ -278,12 +272,12 @@ static int pcipcwd_start(void)
spin_unlock(&pcipcwd_private.io_lock);
if (stat_reg & WD_PCI_WDIS) {
- printk(KERN_ERR PFX "Card timer not enabled\n");
+ pr_err("Card timer not enabled\n");
return -1;
}
if (debug >= VERBOSE)
- printk(KERN_DEBUG PFX "Watchdog started\n");
+ pr_debug("Watchdog started\n");
return 0;
}
@@ -303,13 +297,12 @@ static int pcipcwd_stop(void)
spin_unlock(&pcipcwd_private.io_lock);
if (!(stat_reg & WD_PCI_WDIS)) {
- printk(KERN_ERR PFX
- "Card did not acknowledge disable attempt\n");
+ pr_err("Card did not acknowledge disable attempt\n");
return -1;
}
if (debug >= VERBOSE)
- printk(KERN_DEBUG PFX "Watchdog stopped\n");
+ pr_debug("Watchdog stopped\n");
return 0;
}
@@ -322,7 +315,7 @@ static int pcipcwd_keepalive(void)
spin_unlock(&pcipcwd_private.io_lock);
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n");
+ pr_debug("Watchdog keepalive signal send\n");
return 0;
}
@@ -340,8 +333,7 @@ static int pcipcwd_set_heartbeat(int t)
heartbeat = t;
if (debug >= VERBOSE)
- printk(KERN_DEBUG PFX "New heartbeat: %d\n",
- heartbeat);
+ pr_debug("New heartbeat: %d\n", heartbeat);
return 0;
}
@@ -357,12 +349,11 @@ static int pcipcwd_get_status(int *status)
if (control_status & WD_PCI_TTRP) {
*status |= WDIOF_OVERHEAT;
if (temp_panic)
- panic(PFX "Temperature overheat trip!\n");
+ panic(KBUILD_MODNAME ": Temperature overheat trip!\n");
}
if (debug >= DEBUG)
- printk(KERN_DEBUG PFX "Control Status #1: 0x%02x\n",
- control_status);
+ pr_debug("Control Status #1: 0x%02x\n", control_status);
return 0;
}
@@ -374,14 +365,14 @@ static int pcipcwd_clear_status(void)
int reset_counter;
if (debug >= VERBOSE)
- printk(KERN_INFO PFX "clearing watchdog trip status & LED\n");
+ pr_info("clearing watchdog trip status & LED\n");
control_status = inb_p(pcipcwd_private.io_addr + 1);
if (debug >= DEBUG) {
- printk(KERN_DEBUG PFX "status was: 0x%02x\n", control_status);
- printk(KERN_DEBUG PFX "sending: 0x%02x\n",
- (control_status & WD_PCI_R2DS) | WD_PCI_WTRP);
+ pr_debug("status was: 0x%02x\n", control_status);
+ pr_debug("sending: 0x%02x\n",
+ (control_status & WD_PCI_R2DS) | WD_PCI_WTRP);
}
/* clear trip status & LED and keep mode of relay 2 */
@@ -394,8 +385,7 @@ static int pcipcwd_clear_status(void)
send_command(CMD_GET_CLEAR_RESET_COUNT, &msb, &reset_counter);
if (debug >= DEBUG) {
- printk(KERN_DEBUG PFX "reset count was: 0x%02x\n",
- reset_counter);
+ pr_debug("reset count was: 0x%02x\n", reset_counter);
}
return 0;
@@ -418,8 +408,7 @@ static int pcipcwd_get_temperature(int *temperature)
*temperature = (*temperature * 9 / 5) + 32;
if (debug >= DEBUG) {
- printk(KERN_DEBUG PFX "temperature is: %d F\n",
- *temperature);
+ pr_debug("temperature is: %d F\n", *temperature);
}
return 0;
@@ -437,8 +426,7 @@ static int pcipcwd_get_timeleft(int *time_left)
*time_left = (msb << 8) + lsb;
if (debug >= VERBOSE)
- printk(KERN_DEBUG PFX "Time left before next reboot: %d\n",
- *time_left);
+ pr_debug("Time left before next reboot: %d\n", *time_left);
return 0;
}
@@ -583,8 +571,7 @@ static int pcipcwd_open(struct inode *inode, struct file *file)
/* /dev/watchdog can only be opened once */
if (test_and_set_bit(0, &is_active)) {
if (debug >= VERBOSE)
- printk(KERN_ERR PFX
- "Attempt to open already opened device.\n");
+ pr_err("Attempt to open already opened device\n");
return -EBUSY;
}
@@ -602,8 +589,7 @@ static int pcipcwd_release(struct inode *inode, struct file *file)
if (expect_release == 42) {
pcipcwd_stop();
} else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
pcipcwd_keepalive();
}
expect_release = 0;
@@ -703,20 +689,20 @@ static int __devinit pcipcwd_card_init(struct pci_dev *pdev,
cards_found++;
if (cards_found == 1)
- printk(KERN_INFO PFX DRIVER_VERSION);
+ pr_info("%s\n", DRIVER_VERSION);
if (cards_found > 1) {
- printk(KERN_ERR PFX "This driver only supports 1 device\n");
+ pr_err("This driver only supports 1 device\n");
return -ENODEV;
}
if (pci_enable_device(pdev)) {
- printk(KERN_ERR PFX "Not possible to enable PCI Device\n");
+ pr_err("Not possible to enable PCI Device\n");
return -ENODEV;
}
if (pci_resource_start(pdev, 0) == 0x0000) {
- printk(KERN_ERR PFX "No I/O-Address for card detected\n");
+ pr_err("No I/O-Address for card detected\n");
ret = -ENODEV;
goto err_out_disable_device;
}
@@ -725,8 +711,8 @@ static int __devinit pcipcwd_card_init(struct pci_dev *pdev,
pcipcwd_private.io_addr = pci_resource_start(pdev, 0);
if (pci_request_regions(pdev, WATCHDOG_NAME)) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
- (int) pcipcwd_private.io_addr);
+ pr_err("I/O address 0x%04x already in use\n",
+ (int) pcipcwd_private.io_addr);
ret = -EIO;
goto err_out_disable_device;
}
@@ -755,36 +741,33 @@ static int __devinit pcipcwd_card_init(struct pci_dev *pdev,
* if not reset to the default */
if (pcipcwd_set_heartbeat(heartbeat)) {
pcipcwd_set_heartbeat(WATCHDOG_HEARTBEAT);
- printk(KERN_INFO PFX
- "heartbeat value must be 0<heartbeat<65536, using %d\n",
+ pr_info("heartbeat value must be 0<heartbeat<65536, using %d\n",
WATCHDOG_HEARTBEAT);
}
ret = register_reboot_notifier(&pcipcwd_notifier);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
goto err_out_release_region;
}
if (pcipcwd_private.supports_temp) {
ret = misc_register(&pcipcwd_temp_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX "cannot register miscdev on "
- "minor=%d (err=%d)\n", TEMP_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ TEMP_MINOR, ret);
goto err_out_unregister_reboot;
}
}
ret = misc_register(&pcipcwd_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto err_out_misc_deregister;
}
- printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
+ pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
heartbeat, nowayout);
return 0;
@@ -842,7 +825,7 @@ static void __exit pcipcwd_cleanup_module(void)
{
pci_unregister_driver(&pcipcwd_driver);
- printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+ pr_info("Watchdog Module Unloaded\n");
}
module_init(pcipcwd_init_module);
diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c
index d8de1dd..7b14d18 100644
--- a/drivers/watchdog/pcwd_usb.c
+++ b/drivers/watchdog/pcwd_usb.c
@@ -24,6 +24,8 @@
* http://www.berkprod.com/ or http://www.pcwatchdog.com/
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h> /* For module specific items */
#include <linux/moduleparam.h> /* For new moduleparam's */
#include <linux/types.h> /* For standard types (like size_t) */
@@ -42,17 +44,23 @@
#include <linux/hid.h> /* For HID_REQ_SET_REPORT & HID_DT_REPORT */
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
-
#ifdef CONFIG_USB_DEBUG
- static int debug = 1;
+static int debug = 1;
#else
- static int debug;
+static int debug;
#endif
/* Use our own dbg macro */
+
#undef dbg
-#define dbg(format, arg...) \
- do { if (debug) printk(KERN_DEBUG PFX format "\n" , ## arg); } while (0)
+#ifndef DEBUG
+#define DEBUG
+#endif
+#define dbg(format, ...) \
+do { \
+ if (debug) \
+ pr_debug(format "\n", ##__VA_ARGS__); \
+} while (0)
/* Module and Version Information */
#define DRIVER_VERSION "1.02"
@@ -60,7 +68,6 @@
#define DRIVER_DESC "Berkshire USB-PC Watchdog driver"
#define DRIVER_LICENSE "GPL"
#define DRIVER_NAME "pcwd_usb"
-#define PFX DRIVER_NAME ": "
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
@@ -80,8 +87,8 @@ MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
"(0<heartbeat<65536 or 0=delay-time from dip-switches, default="
__MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -220,8 +227,8 @@ static void usb_pcwd_intr_done(struct urb *urb)
resubmit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
- printk(KERN_ERR PFX "can't resubmit intr, "
- "usb_submit_urb failed with result %d\n", retval);
+ pr_err("can't resubmit intr, usb_submit_urb failed with result %d\n",
+ retval);
}
static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd,
@@ -284,8 +291,7 @@ static int usb_pcwd_start(struct usb_pcwd_private *usb_pcwd)
&msb, &lsb);
if ((retval == 0) || (lsb == 0)) {
- printk(KERN_ERR PFX
- "Card did not acknowledge enable attempt\n");
+ pr_err("Card did not acknowledge enable attempt\n");
return -1;
}
@@ -303,8 +309,7 @@ static int usb_pcwd_stop(struct usb_pcwd_private *usb_pcwd)
&msb, &lsb);
if ((retval == 0) || (lsb != 0)) {
- printk(KERN_ERR PFX
- "Card did not acknowledge disable attempt\n");
+ pr_err("Card did not acknowledge disable attempt\n");
return -1;
}
@@ -506,8 +511,7 @@ static int usb_pcwd_release(struct inode *inode, struct file *file)
if (expect_release == 42) {
usb_pcwd_stop(usb_pcwd_device);
} else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
usb_pcwd_keepalive(usb_pcwd_device);
}
expect_release = 0;
@@ -627,7 +631,7 @@ static int usb_pcwd_probe(struct usb_interface *interface,
cards_found++;
if (cards_found > 1) {
- printk(KERN_ERR PFX "This driver only supports 1 device\n");
+ pr_err("This driver only supports 1 device\n");
return -ENODEV;
}
@@ -636,8 +640,7 @@ static int usb_pcwd_probe(struct usb_interface *interface,
/* check out that we have a HID device */
if (!(iface_desc->desc.bInterfaceClass == USB_CLASS_HID)) {
- printk(KERN_ERR PFX
- "The device isn't a Human Interface Device\n");
+ pr_err("The device isn't a Human Interface Device\n");
return -ENODEV;
}
@@ -646,7 +649,7 @@ static int usb_pcwd_probe(struct usb_interface *interface,
if (!usb_endpoint_is_int_in(endpoint)) {
/* we didn't find a Interrupt endpoint with direction IN */
- printk(KERN_ERR PFX "Couldn't find an INTR & IN endpoint\n");
+ pr_err("Couldn't find an INTR & IN endpoint\n");
return -ENODEV;
}
@@ -657,7 +660,7 @@ static int usb_pcwd_probe(struct usb_interface *interface,
/* allocate memory for our device and initialize it */
usb_pcwd = kzalloc(sizeof(struct usb_pcwd_private), GFP_KERNEL);
if (usb_pcwd == NULL) {
- printk(KERN_ERR PFX "Out of memory\n");
+ pr_err("Out of memory\n");
goto error;
}
@@ -674,14 +677,14 @@ static int usb_pcwd_probe(struct usb_interface *interface,
usb_pcwd->intr_buffer = usb_alloc_coherent(udev, usb_pcwd->intr_size,
GFP_ATOMIC, &usb_pcwd->intr_dma);
if (!usb_pcwd->intr_buffer) {
- printk(KERN_ERR PFX "Out of memory\n");
+ pr_err("Out of memory\n");
goto error;
}
/* allocate the urb's */
usb_pcwd->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!usb_pcwd->intr_urb) {
- printk(KERN_ERR PFX "Out of memory\n");
+ pr_err("Out of memory\n");
goto error;
}
@@ -694,7 +697,7 @@ static int usb_pcwd_probe(struct usb_interface *interface,
/* register our interrupt URB with the USB system */
if (usb_submit_urb(usb_pcwd->intr_urb, GFP_KERNEL)) {
- printk(KERN_ERR PFX "Problem registering interrupt URB\n");
+ pr_err("Problem registering interrupt URB\n");
retval = -EIO; /* failure */
goto error;
}
@@ -713,15 +716,13 @@ static int usb_pcwd_probe(struct usb_interface *interface,
else
sprintf(fw_ver_str, "<card no answer>");
- printk(KERN_INFO PFX "Found card (Firmware: %s) with temp option\n",
- fw_ver_str);
+ pr_info("Found card (Firmware: %s) with temp option\n", fw_ver_str);
/* Get switch settings */
usb_pcwd_send_command(usb_pcwd, CMD_GET_DIP_SWITCH_SETTINGS, &dummy,
&option_switches);
- printk(KERN_INFO PFX "Option switches (0x%02x): "
- "Temperature Reset Enable=%s, Power On Delay=%s\n",
+ pr_info("Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
option_switches,
((option_switches & 0x10) ? "ON" : "OFF"),
((option_switches & 0x08) ? "ON" : "OFF"));
@@ -734,39 +735,34 @@ static int usb_pcwd_probe(struct usb_interface *interface,
* if not reset to the default */
if (usb_pcwd_set_heartbeat(usb_pcwd, heartbeat)) {
usb_pcwd_set_heartbeat(usb_pcwd, WATCHDOG_HEARTBEAT);
- printk(KERN_INFO PFX
- "heartbeat value must be 0<heartbeat<65536, using %d\n",
+ pr_info("heartbeat value must be 0<heartbeat<65536, using %d\n",
WATCHDOG_HEARTBEAT);
}
retval = register_reboot_notifier(&usb_pcwd_notifier);
if (retval != 0) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n",
- retval);
+ pr_err("cannot register reboot notifier (err=%d)\n", retval);
goto error;
}
retval = misc_register(&usb_pcwd_temperature_miscdev);
if (retval != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- TEMP_MINOR, retval);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ TEMP_MINOR, retval);
goto err_out_unregister_reboot;
}
retval = misc_register(&usb_pcwd_miscdev);
if (retval != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, retval);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, retval);
goto err_out_misc_deregister;
}
/* we can register the device now, as it is ready */
usb_set_intfdata(interface, usb_pcwd);
- printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
+ pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
heartbeat, nowayout);
return 0;
@@ -824,7 +820,7 @@ static void usb_pcwd_disconnect(struct usb_interface *interface)
mutex_unlock(&disconnect_mutex);
- printk(KERN_INFO PFX "USB PC Watchdog disconnected\n");
+ pr_info("USB PC Watchdog disconnected\n");
}
module_usb_driver(usb_pcwd_driver);
diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c
index 2d22e99..7d3d471 100644
--- a/drivers/watchdog/pika_wdt.c
+++ b/drivers/watchdog/pika_wdt.c
@@ -5,6 +5,8 @@
* Sean MacLennan <smaclennan@pikatech.com>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/module.h>
@@ -23,7 +25,6 @@
#include <linux/of_platform.h>
#define DRV_NAME "PIKA-WDT"
-#define PFX DRV_NAME ": "
/* Hardware timeout in seconds */
#define WDT_HW_TIMEOUT 2
@@ -38,8 +39,8 @@ module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -90,7 +91,7 @@ static void pikawdt_ping(unsigned long data)
pikawdt_reset();
mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
} else
- printk(KERN_CRIT PFX "I will reset your machine !\n");
+ pr_crit("I will reset your machine !\n");
}
@@ -228,14 +229,14 @@ static int __init pikawdt_init(void)
np = of_find_compatible_node(NULL, NULL, "pika,fpga");
if (np == NULL) {
- printk(KERN_ERR PFX "Unable to find fpga.\n");
+ pr_err("Unable to find fpga\n");
return -ENOENT;
}
pikawdt_private.fpga = of_iomap(np, 0);
of_node_put(np);
if (pikawdt_private.fpga == NULL) {
- printk(KERN_ERR PFX "Unable to map fpga.\n");
+ pr_err("Unable to map fpga\n");
return -ENOMEM;
}
@@ -244,7 +245,7 @@ static int __init pikawdt_init(void)
/* POST information is in the sd area. */
np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd");
if (np == NULL) {
- printk(KERN_ERR PFX "Unable to find fpga-sd.\n");
+ pr_err("Unable to find fpga-sd\n");
ret = -ENOENT;
goto out;
}
@@ -252,7 +253,7 @@ static int __init pikawdt_init(void)
fpga = of_iomap(np, 0);
of_node_put(np);
if (fpga == NULL) {
- printk(KERN_ERR PFX "Unable to map fpga-sd.\n");
+ pr_err("Unable to map fpga-sd\n");
ret = -ENOMEM;
goto out;
}
@@ -271,12 +272,12 @@ static int __init pikawdt_init(void)
ret = misc_register(&pikawdt_miscdev);
if (ret) {
- printk(KERN_ERR PFX "Unable to register miscdev.\n");
+ pr_err("Unable to register miscdev\n");
goto out;
}
- printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
- heartbeat, nowayout);
+ pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
+ heartbeat, nowayout);
return 0;
out:
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
index dfae030..6b8432f 100644
--- a/drivers/watchdog/pnx4008_wdt.c
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -8,33 +8,32 @@
* Based on sa1100 driver,
* Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
*
- * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
+ * 2005-2006 (c) MontaVista Software, Inc.
+ *
+ * (C) 2012 Wolfram Sang, Pengutronix
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/init.h>
-#include <linux/bitops.h>
-#include <linux/ioport.h>
-#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/spinlock.h>
-#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/err.h>
#include <mach/hardware.h>
-#define MODULE_NAME "PNX4008-WDT: "
-
/* WatchDog Timer - Chapter 23 Page 207 */
#define DEFAULT_HEARTBEAT 19
@@ -77,251 +76,128 @@
#define WDOG_COUNTER_RATE 13000000 /*the counter clock is 13 MHz fixed */
-static int nowayout = WATCHDOG_NOWAYOUT;
-static int heartbeat = DEFAULT_HEARTBEAT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
+static unsigned int heartbeat = DEFAULT_HEARTBEAT;
static DEFINE_SPINLOCK(io_lock);
-static unsigned long wdt_status;
-#define WDT_IN_USE 0
-#define WDT_OK_TO_CLOSE 1
-#define WDT_REGION_INITED 2
-#define WDT_DEVICE_INITED 3
-
-static unsigned long boot_status;
-
-static struct resource *wdt_mem;
static void __iomem *wdt_base;
struct clk *wdt_clk;
-static void wdt_enable(void)
+static int pnx4008_wdt_start(struct watchdog_device *wdd)
{
spin_lock(&io_lock);
/* stop counter, initiate counter reset */
- __raw_writel(RESET_COUNT, WDTIM_CTRL(wdt_base));
+ writel(RESET_COUNT, WDTIM_CTRL(wdt_base));
/*wait for reset to complete. 100% guarantee event */
- while (__raw_readl(WDTIM_COUNTER(wdt_base)))
+ while (readl(WDTIM_COUNTER(wdt_base)))
cpu_relax();
/* internal and external reset, stop after that */
- __raw_writel(M_RES2 | STOP_COUNT0 | RESET_COUNT0,
- WDTIM_MCTRL(wdt_base));
+ writel(M_RES2 | STOP_COUNT0 | RESET_COUNT0, WDTIM_MCTRL(wdt_base));
/* configure match output */
- __raw_writel(MATCH_OUTPUT_HIGH, WDTIM_EMR(wdt_base));
+ writel(MATCH_OUTPUT_HIGH, WDTIM_EMR(wdt_base));
/* clear interrupt, just in case */
- __raw_writel(MATCH_INT, WDTIM_INT(wdt_base));
+ writel(MATCH_INT, WDTIM_INT(wdt_base));
/* the longest pulse period 65541/(13*10^6) seconds ~ 5 ms. */
- __raw_writel(0xFFFF, WDTIM_PULSE(wdt_base));
- __raw_writel(heartbeat * WDOG_COUNTER_RATE, WDTIM_MATCH0(wdt_base));
+ writel(0xFFFF, WDTIM_PULSE(wdt_base));
+ writel(wdd->timeout * WDOG_COUNTER_RATE, WDTIM_MATCH0(wdt_base));
/*enable counter, stop when debugger active */
- __raw_writel(COUNT_ENAB | DEBUG_EN, WDTIM_CTRL(wdt_base));
+ writel(COUNT_ENAB | DEBUG_EN, WDTIM_CTRL(wdt_base));
spin_unlock(&io_lock);
+ return 0;
}
-static void wdt_disable(void)
+static int pnx4008_wdt_stop(struct watchdog_device *wdd)
{
spin_lock(&io_lock);
- __raw_writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */
+ writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */
spin_unlock(&io_lock);
+ return 0;
}
-static int pnx4008_wdt_open(struct inode *inode, struct file *file)
-{
- int ret;
-
- if (test_and_set_bit(WDT_IN_USE, &wdt_status))
- return -EBUSY;
-
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
- ret = clk_enable(wdt_clk);
- if (ret) {
- clear_bit(WDT_IN_USE, &wdt_status);
- return ret;
- }
-
- wdt_enable();
-
- return nonseekable_open(inode, file);
-}
-
-static ssize_t pnx4008_wdt_write(struct file *file, const char *data,
- size_t len, loff_t *ppos)
+static int pnx4008_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int new_timeout)
{
- if (len) {
- if (!nowayout) {
- size_t i;
-
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
- for (i = 0; i != len; i++) {
- char c;
-
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- set_bit(WDT_OK_TO_CLOSE, &wdt_status);
- }
- }
- wdt_enable();
- }
-
- return len;
+ wdd->timeout = new_timeout;
+ return 0;
}
-static const struct watchdog_info ident = {
+static const struct watchdog_info pnx4008_wdt_ident = {
.options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
.identity = "PNX4008 Watchdog",
};
-static long pnx4008_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int ret = -ENOTTY;
- int time;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = copy_to_user((struct watchdog_info *)arg, &ident,
- sizeof(ident)) ? -EFAULT : 0;
- break;
-
- case WDIOC_GETSTATUS:
- ret = put_user(0, (int *)arg);
- break;
-
- case WDIOC_GETBOOTSTATUS:
- ret = put_user(boot_status, (int *)arg);
- break;
-
- case WDIOC_KEEPALIVE:
- wdt_enable();
- ret = 0;
- break;
-
- case WDIOC_SETTIMEOUT:
- ret = get_user(time, (int *)arg);
- if (ret)
- break;
-
- if (time <= 0 || time > MAX_HEARTBEAT) {
- ret = -EINVAL;
- break;
- }
-
- heartbeat = time;
- wdt_enable();
- /* Fall through */
-
- case WDIOC_GETTIMEOUT:
- ret = put_user(heartbeat, (int *)arg);
- break;
- }
- return ret;
-}
-
-static int pnx4008_wdt_release(struct inode *inode, struct file *file)
-{
- if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status))
- printk(KERN_WARNING "WATCHDOG: Device closed unexpectedly\n");
-
- wdt_disable();
- clk_disable(wdt_clk);
- clear_bit(WDT_IN_USE, &wdt_status);
- clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
- return 0;
-}
-
-static const struct file_operations pnx4008_wdt_fops = {
+static const struct watchdog_ops pnx4008_wdt_ops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = pnx4008_wdt_write,
- .unlocked_ioctl = pnx4008_wdt_ioctl,
- .open = pnx4008_wdt_open,
- .release = pnx4008_wdt_release,
+ .start = pnx4008_wdt_start,
+ .stop = pnx4008_wdt_stop,
+ .set_timeout = pnx4008_wdt_set_timeout,
};
-static struct miscdevice pnx4008_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &pnx4008_wdt_fops,
+static struct watchdog_device pnx4008_wdd = {
+ .info = &pnx4008_wdt_ident,
+ .ops = &pnx4008_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = MAX_HEARTBEAT,
};
static int __devinit pnx4008_wdt_probe(struct platform_device *pdev)
{
- int ret = 0, size;
+ struct resource *r;
+ int ret = 0;
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
heartbeat = DEFAULT_HEARTBEAT;
- printk(KERN_INFO MODULE_NAME
- "PNX4008 Watchdog Timer: heartbeat %d sec\n", heartbeat);
-
- wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (wdt_mem == NULL) {
- printk(KERN_INFO MODULE_NAME
- "failed to get memory region resource\n");
- return -ENOENT;
- }
-
- size = resource_size(wdt_mem);
-
- if (!request_mem_region(wdt_mem->start, size, pdev->name)) {
- printk(KERN_INFO MODULE_NAME "failed to get memory region\n");
- return -ENOENT;
- }
- wdt_base = (void __iomem *)IO_ADDRESS(wdt_mem->start);
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ wdt_base = devm_request_and_ioremap(&pdev->dev, r);
+ if (!wdt_base)
+ return -EADDRINUSE;
wdt_clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(wdt_clk)) {
- ret = PTR_ERR(wdt_clk);
- release_mem_region(wdt_mem->start, size);
- wdt_mem = NULL;
- goto out;
- }
+ if (IS_ERR(wdt_clk))
+ return PTR_ERR(wdt_clk);
ret = clk_enable(wdt_clk);
- if (ret) {
- release_mem_region(wdt_mem->start, size);
- wdt_mem = NULL;
- clk_put(wdt_clk);
+ if (ret)
goto out;
- }
- ret = misc_register(&pnx4008_wdt_miscdev);
+ pnx4008_wdd.timeout = heartbeat;
+ pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
+ WDIOF_CARDRESET : 0;
+ watchdog_set_nowayout(&pnx4008_wdd, nowayout);
+
+ pnx4008_wdt_stop(&pnx4008_wdd); /* disable for now */
+
+ ret = watchdog_register_device(&pnx4008_wdd);
if (ret < 0) {
- printk(KERN_ERR MODULE_NAME "cannot register misc device\n");
- release_mem_region(wdt_mem->start, size);
- wdt_mem = NULL;
- clk_disable(wdt_clk);
- clk_put(wdt_clk);
- } else {
- boot_status = (__raw_readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
- WDIOF_CARDRESET : 0;
- wdt_disable(); /*disable for now */
- clk_disable(wdt_clk);
- set_bit(WDT_DEVICE_INITED, &wdt_status);
+ dev_err(&pdev->dev, "cannot register watchdog device\n");
+ goto disable_clk;
}
+ dev_info(&pdev->dev, "PNX4008 Watchdog Timer: heartbeat %d sec\n",
+ heartbeat);
+
+ return 0;
+
+disable_clk:
+ clk_disable(wdt_clk);
out:
+ clk_put(wdt_clk);
return ret;
}
static int __devexit pnx4008_wdt_remove(struct platform_device *pdev)
{
- misc_deregister(&pnx4008_wdt_miscdev);
+ watchdog_unregister_device(&pnx4008_wdd);
clk_disable(wdt_clk);
clk_put(wdt_clk);
- if (wdt_mem) {
- release_mem_region(wdt_mem->start, resource_size(wdt_mem));
- wdt_mem = NULL;
- }
return 0;
}
@@ -337,15 +213,16 @@ static struct platform_driver platform_wdt_driver = {
module_platform_driver(platform_wdt_driver);
MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
+MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
MODULE_DESCRIPTION("PNX4008 Watchdog Driver");
-module_param(heartbeat, int, 0);
+module_param(heartbeat, uint, 0);
MODULE_PARM_DESC(heartbeat,
"Watchdog heartbeat period in seconds from 1 to "
__MODULE_STRING(MAX_HEARTBEAT) ", default "
__MODULE_STRING(DEFAULT_HEARTBEAT));
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Set to 1 to keep watchdog running after device release");
diff --git a/drivers/watchdog/pnx833x_wdt.c b/drivers/watchdog/pnx833x_wdt.c
index a7b5ad2..1b62a7d 100644
--- a/drivers/watchdog/pnx833x_wdt.c
+++ b/drivers/watchdog/pnx833x_wdt.c
@@ -17,6 +17,8 @@
* based on softdog.c by Alan Cox <alan@redhat.com>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -30,7 +32,6 @@
#include <linux/init.h>
#include <asm/mach-pnx833x/pnx833x.h>
-#define PFX "pnx833x: "
#define WATCHDOG_TIMEOUT 30 /* 30 sec Maximum timeout */
#define WATCHDOG_COUNT_FREQUENCY 68000000U /* Watchdog counts at 68MHZ. */
#define PNX_WATCHDOG_TIMEOUT (WATCHDOG_TIMEOUT * WATCHDOG_COUNT_FREQUENCY)
@@ -54,8 +55,8 @@ module_param(pnx833x_wdt_timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in Mhz. (68Mhz clock), default="
__MODULE_STRING(PNX_TIMEOUT_VALUE) "(30 seconds).");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -76,7 +77,7 @@ static void pnx833x_wdt_start(void)
PNX833X_REG(PNX833X_CONFIG +
PNX833X_CONFIG_CPU_COUNTERS_CONTROL) |= 0x1;
- printk(KERN_INFO PFX "Started watchdog timer.\n");
+ pr_info("Started watchdog timer\n");
}
static void pnx833x_wdt_stop(void)
@@ -87,7 +88,7 @@ static void pnx833x_wdt_stop(void)
PNX833X_REG(PNX833X_CONFIG +
PNX833X_CONFIG_CPU_COUNTERS_CONTROL) &= 0xFFFFFFFE;
- printk(KERN_INFO PFX "Stopped watchdog timer.\n");
+ pr_info("Stopped watchdog timer\n");
}
static void pnx833x_wdt_ping(void)
@@ -113,7 +114,7 @@ static int pnx833x_wdt_open(struct inode *inode, struct file *file)
pnx833x_wdt_ping();
- printk(KERN_INFO "Started watchdog timer.\n");
+ pr_info("Started watchdog timer\n");
return nonseekable_open(inode, file);
}
@@ -232,9 +233,6 @@ static struct notifier_block pnx833x_wdt_notifier = {
.notifier_call = pnx833x_wdt_notify_sys,
};
-static char banner[] __initdata =
- KERN_INFO PFX "Hardware Watchdog Timer for PNX833x: Version 0.1\n";
-
static int __init watchdog_init(void)
{
int ret, cause;
@@ -243,27 +241,25 @@ static int __init watchdog_init(void)
cause = PNX833X_REG(PNX833X_RESET);
/*If bit 31 is set then watchdog was cause of reset.*/
if (cause & 0x80000000) {
- printk(KERN_INFO PFX "The system was previously reset due to "
- "the watchdog firing - please investigate...\n");
+ pr_info("The system was previously reset due to the watchdog firing - please investigate...\n");
}
ret = register_reboot_notifier(&pnx833x_wdt_notifier);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
return ret;
}
ret = misc_register(&pnx833x_wdt_miscdev);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
unregister_reboot_notifier(&pnx833x_wdt_notifier);
return ret;
}
- printk(banner);
+ pr_info("Hardware Watchdog Timer for PNX833x: Version 0.1\n");
+
if (start_enabled)
pnx833x_wdt_start();
diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c
index bf7bc8a..547353a 100644
--- a/drivers/watchdog/rc32434_wdt.c
+++ b/drivers/watchdog/rc32434_wdt.c
@@ -17,6 +17,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h> /* For module specific items */
#include <linux/moduleparam.h> /* For new moduleparam's */
#include <linux/types.h> /* For standard types (like size_t) */
@@ -33,8 +35,6 @@
#include <asm/mach-rc32434/integ.h> /* For the Watchdog registers */
-#define PFX KBUILD_MODNAME ": "
-
#define VERSION "1.0"
static struct {
@@ -64,8 +64,8 @@ module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout value, in seconds (default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -78,8 +78,7 @@ static int rc32434_wdt_set(int new_timeout)
int max_to = WTCOMP2SEC((u32)-1);
if (new_timeout < 0 || new_timeout > max_to) {
- printk(KERN_ERR PFX "timeout value must be between 0 and %d",
- max_to);
+ pr_err("timeout value must be between 0 and %d\n", max_to);
return -EINVAL;
}
timeout = new_timeout;
@@ -119,7 +118,7 @@ static void rc32434_wdt_start(void)
SET_BITS(wdt_reg->wtc, or, nand);
spin_unlock(&rc32434_wdt_device.io_lock);
- printk(KERN_INFO PFX "Started watchdog timer.\n");
+ pr_info("Started watchdog timer\n");
}
static void rc32434_wdt_stop(void)
@@ -130,7 +129,7 @@ static void rc32434_wdt_stop(void)
SET_BITS(wdt_reg->wtc, 0, 1 << RC32434_WTC_EN);
spin_unlock(&rc32434_wdt_device.io_lock);
- printk(KERN_INFO PFX "Stopped watchdog timer.\n");
+ pr_info("Stopped watchdog timer\n");
}
static void rc32434_wdt_ping(void)
@@ -160,8 +159,7 @@ static int rc32434_wdt_release(struct inode *inode, struct file *file)
rc32434_wdt_stop();
module_put(THIS_MODULE);
} else {
- printk(KERN_CRIT PFX
- "device closed unexpectedly. WDT will not stop!\n");
+ pr_crit("device closed unexpectedly. WDT will not stop!\n");
rc32434_wdt_ping();
}
clear_bit(0, &rc32434_wdt_device.inuse);
@@ -262,9 +260,6 @@ static struct miscdevice rc32434_wdt_miscdev = {
.fops = &rc32434_wdt_fops,
};
-static char banner[] __devinitdata = KERN_INFO PFX
- "Watchdog Timer version " VERSION ", timer margin: %d sec\n";
-
static int __devinit rc32434_wdt_probe(struct platform_device *pdev)
{
int ret;
@@ -272,13 +267,13 @@ static int __devinit rc32434_wdt_probe(struct platform_device *pdev)
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb532_wdt_res");
if (!r) {
- printk(KERN_ERR PFX "failed to retrieve resources\n");
+ pr_err("failed to retrieve resources\n");
return -ENODEV;
}
wdt_reg = ioremap_nocache(r->start, resource_size(r));
if (!wdt_reg) {
- printk(KERN_ERR PFX "failed to remap I/O resources\n");
+ pr_err("failed to remap I/O resources\n");
return -ENXIO;
}
@@ -291,18 +286,18 @@ static int __devinit rc32434_wdt_probe(struct platform_device *pdev)
* if not reset to the default */
if (rc32434_wdt_set(timeout)) {
rc32434_wdt_set(WATCHDOG_TIMEOUT);
- printk(KERN_INFO PFX
- "timeout value must be between 0 and %d\n",
+ pr_info("timeout value must be between 0 and %d\n",
WTCOMP2SEC((u32)-1));
}
ret = misc_register(&rc32434_wdt_miscdev);
if (ret < 0) {
- printk(KERN_ERR PFX "failed to register watchdog device\n");
+ pr_err("failed to register watchdog device\n");
goto unmap;
}
- printk(banner, timeout);
+ pr_info("Watchdog Timer version " VERSION ", timer margin: %d sec\n",
+ timeout);
return 0;
diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c
index c7e17ce..49e1b1c 100644
--- a/drivers/watchdog/riowd.c
+++ b/drivers/watchdog/riowd.c
@@ -3,6 +3,8 @@
* Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net)
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -189,7 +191,7 @@ static int __devinit riowd_probe(struct platform_device *op)
p->regs = of_ioremap(&op->resource[0], 0, 2, DRIVER_NAME);
if (!p->regs) {
- printk(KERN_ERR PFX "Cannot map registers.\n");
+ pr_err("Cannot map registers\n");
goto out_free;
}
/* Make miscdev useable right away */
@@ -197,12 +199,12 @@ static int __devinit riowd_probe(struct platform_device *op)
err = misc_register(&riowd_miscdev);
if (err) {
- printk(KERN_ERR PFX "Cannot register watchdog misc device.\n");
+ pr_err("Cannot register watchdog misc device\n");
goto out_iounmap;
}
- printk(KERN_INFO PFX "Hardware watchdog [%i minutes], "
- "regs at %p\n", riowd_timeout, p->regs);
+ pr_info("Hardware watchdog [%i minutes], regs at %p\n",
+ riowd_timeout, p->regs);
dev_set_drvdata(&op->dev, p);
return 0;
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 404172f..04e5a6d 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -23,6 +23,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -46,12 +48,10 @@
#include <plat/regs-watchdog.h>
-#define PFX "s3c2410-wdt: "
-
#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0)
#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15)
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static int tmr_margin = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;
static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT;
static int soft_noboot;
@@ -59,7 +59,7 @@ static int debug;
module_param(tmr_margin, int, 0);
module_param(tmr_atboot, int, 0);
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
module_param(soft_noboot, int, 0);
module_param(debug, int, 0);
@@ -84,10 +84,11 @@ static DEFINE_SPINLOCK(wdt_lock);
/* watchdog control routines */
-#define DBG(msg...) do { \
- if (debug) \
- printk(KERN_INFO msg); \
- } while (0)
+#define DBG(fmt, ...) \
+do { \
+ if (debug) \
+ pr_info(fmt, ##__VA_ARGS__); \
+} while (0)
/* functions */
@@ -200,6 +201,8 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou
writel(count, wdt_base + S3C2410_WTDAT);
writel(wtcon, wdt_base + S3C2410_WTCON);
+ wdd->timeout = timeout;
+
return 0;
}
@@ -354,7 +357,7 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
ret = s3c2410wdt_cpufreq_register();
if (ret < 0) {
- printk(KERN_ERR PFX "failed to register cpufreq\n");
+ pr_err("failed to register cpufreq\n");
goto err_clk;
}
@@ -483,8 +486,8 @@ static int s3c2410wdt_resume(struct platform_device *dev)
writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */
writel(wtcon_save, wdt_base + S3C2410_WTCON);
- printk(KERN_INFO PFX "watchdog %sabled\n",
- (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
+ pr_info("watchdog %sabled\n",
+ (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
return 0;
}
@@ -518,12 +521,10 @@ static struct platform_driver s3c2410wdt_driver = {
};
-static char banner[] __initdata =
- KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";
-
static int __init watchdog_init(void)
{
- printk(banner);
+ pr_info("S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n");
+
return platform_driver_register(&s3c2410wdt_driver);
}
diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c
index 0162454..54984de 100644
--- a/drivers/watchdog/sa1100_wdt.c
+++ b/drivers/watchdog/sa1100_wdt.c
@@ -17,6 +17,9 @@
*
* 27/11/2000 Initial release
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -25,6 +28,7 @@
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <linux/timex.h>
@@ -66,7 +70,7 @@ static int sa1100dog_open(struct inode *inode, struct file *file)
*/
static int sa1100dog_release(struct inode *inode, struct file *file)
{
- printk(KERN_CRIT "WATCHDOG: Device closed - timer will not stop\n");
+ pr_crit("Device closed - timer will not stop\n");
clear_bit(1, &sa1100wdt_users);
return 0;
}
@@ -169,9 +173,8 @@ static int __init sa1100dog_init(void)
ret = misc_register(&sa1100dog_miscdev);
if (ret == 0)
- printk(KERN_INFO
- "SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n",
- margin);
+ pr_info("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n",
+ margin);
return ret;
}
diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c
index b01a30e..25c7a3f 100644
--- a/drivers/watchdog/sb_wdog.c
+++ b/drivers/watchdog/sb_wdog.c
@@ -43,6 +43,9 @@
* version 1 or 2 as published by the Free Software Foundation.
*
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/io.h>
#include <linux/uaccess.h>
@@ -125,9 +128,8 @@ static int sbwdog_release(struct inode *inode, struct file *file)
__raw_writeb(0, user_dog);
module_put(THIS_MODULE);
} else {
- printk(KERN_CRIT
- "%s: Unexpected close, not stopping watchdog!\n",
- ident.identity);
+ pr_crit("%s: Unexpected close, not stopping watchdog!\n",
+ ident.identity);
sbwdog_pet(user_dog);
}
clear_bit(0, &sbwdog_gate);
@@ -269,7 +271,7 @@ irqreturn_t sbwdog_interrupt(int irq, void *addr)
* if it's the second watchdog timer, it's for those users
*/
if (wd_cfg_reg == user_dog)
- printk(KERN_CRIT "%s in danger of initiating system reset "
+ pr_crit("%s in danger of initiating system reset "
"in %ld.%01ld seconds\n",
ident.identity,
wd_init / 1000000, (wd_init / 100000) % 10);
@@ -290,9 +292,8 @@ static int __init sbwdog_init(void)
*/
ret = register_reboot_notifier(&sbwdog_notifier);
if (ret) {
- printk(KERN_ERR
- "%s: cannot register reboot notifier (err=%d)\n",
- ident.identity, ret);
+ pr_err("%s: cannot register reboot notifier (err=%d)\n",
+ ident.identity, ret);
return ret;
}
@@ -303,16 +304,16 @@ static int __init sbwdog_init(void)
ret = request_irq(1, sbwdog_interrupt, IRQF_SHARED,
ident.identity, (void *)user_dog);
if (ret) {
- printk(KERN_ERR "%s: failed to request irq 1 - %d\n",
- ident.identity, ret);
+ pr_err("%s: failed to request irq 1 - %d\n",
+ ident.identity, ret);
goto out;
}
ret = misc_register(&sbwdog_miscdev);
if (ret == 0) {
- printk(KERN_INFO "%s: timeout is %ld.%ld secs\n",
- ident.identity,
- timeout / 1000000, (timeout / 100000) % 10);
+ pr_info("%s: timeout is %ld.%ld secs\n",
+ ident.identity,
+ timeout / 1000000, (timeout / 100000) % 10);
return 0;
}
free_irq(1, (void *)user_dog);
@@ -353,8 +354,7 @@ void platform_wd_setup(void)
ret = request_irq(1, sbwdog_interrupt, IRQF_SHARED,
"Kernel Watchdog", IOADDR(A_SCD_WDOG_CFG_0));
if (ret) {
- printk(KERN_CRIT
- "Watchdog IRQ zero(0) failed to be requested - %d\n", ret);
+ pr_crit("Watchdog IRQ zero(0) failed to be requested - %d\n", ret);
}
}
diff --git a/drivers/watchdog/sbc60xxwdt.c b/drivers/watchdog/sbc60xxwdt.c
index 626d0e8..63632ec 100644
--- a/drivers/watchdog/sbc60xxwdt.c
+++ b/drivers/watchdog/sbc60xxwdt.c
@@ -48,6 +48,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -63,7 +65,6 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#define OUR_NAME "sbc60xxwdt"
#define PFX OUR_NAME ": "
@@ -105,8 +106,8 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. (1<=timeout<=3600, default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -132,8 +133,7 @@ static void wdt_timer_ping(unsigned long data)
/* Re-set the timer interval */
mod_timer(&timer, jiffies + WDT_INTERVAL);
} else
- printk(KERN_WARNING PFX
- "Heartbeat lost! Will not ping the watchdog\n");
+ pr_warn("Heartbeat lost! Will not ping the watchdog\n");
}
/*
@@ -146,7 +146,7 @@ static void wdt_startup(void)
/* Start the timer */
mod_timer(&timer, jiffies + WDT_INTERVAL);
- printk(KERN_INFO PFX "Watchdog timer is now enabled.\n");
+ pr_info("Watchdog timer is now enabled\n");
}
static void wdt_turnoff(void)
@@ -154,7 +154,7 @@ static void wdt_turnoff(void)
/* Stop the timer */
del_timer(&timer);
inb_p(wdt_stop);
- printk(KERN_INFO PFX "Watchdog timer is now disabled...\n");
+ pr_info("Watchdog timer is now disabled...\n");
}
static void wdt_keepalive(void)
@@ -217,8 +217,7 @@ static int fop_close(struct inode *inode, struct file *file)
wdt_turnoff();
else {
del_timer(&timer);
- printk(KERN_CRIT PFX
- "device file closed unexpectedly. Will not stop the WDT!\n");
+ pr_crit("device file closed unexpectedly. Will not stop the WDT!\n");
}
clear_bit(0, &wdt_is_open);
wdt_expect_close = 0;
@@ -335,14 +334,12 @@ static int __init sbc60xxwdt_init(void)
if (timeout < 1 || timeout > 3600) { /* arbitrary upper limit */
timeout = WATCHDOG_TIMEOUT;
- printk(KERN_INFO PFX
- "timeout value must be 1 <= x <= 3600, using %d\n",
- timeout);
+ pr_info("timeout value must be 1 <= x <= 3600, using %d\n",
+ timeout);
}
if (!request_region(wdt_start, 1, "SBC 60XX WDT")) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
- wdt_start);
+ pr_err("I/O address 0x%04x already in use\n", wdt_start);
rc = -EIO;
goto err_out;
}
@@ -350,9 +347,7 @@ static int __init sbc60xxwdt_init(void)
/* We cannot reserve 0x45 - the kernel already has! */
if (wdt_stop != 0x45 && wdt_stop != wdt_start) {
if (!request_region(wdt_stop, 1, "SBC 60XX WDT")) {
- printk(KERN_ERR PFX
- "I/O address 0x%04x already in use\n",
- wdt_stop);
+ pr_err("I/O address 0x%04x already in use\n", wdt_stop);
rc = -EIO;
goto err_out_region1;
}
@@ -360,21 +355,18 @@ static int __init sbc60xxwdt_init(void)
rc = register_reboot_notifier(&wdt_notifier);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", rc);
+ pr_err("cannot register reboot notifier (err=%d)\n", rc);
goto err_out_region2;
}
rc = misc_register(&wdt_miscdev);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- wdt_miscdev.minor, rc);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
goto err_out_reboot;
}
- printk(KERN_INFO PFX
- "WDT driver for 60XX single board computer initialised. "
- "timeout=%d sec (nowayout=%d)\n", timeout, nowayout);
+ pr_info("WDT driver for 60XX single board computer initialised. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
return 0;
diff --git a/drivers/watchdog/sbc7240_wdt.c b/drivers/watchdog/sbc7240_wdt.c
index 93ac589..719edc8 100644
--- a/drivers/watchdog/sbc7240_wdt.c
+++ b/drivers/watchdog/sbc7240_wdt.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/ioport.h>
@@ -30,9 +32,6 @@
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
-#include <asm/system.h>
-
-#define SBC7240_PREFIX "sbc7240_wdt: "
#define SBC7240_ENABLE_PORT 0x443
#define SBC7240_DISABLE_PORT 0x043
@@ -47,8 +46,8 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<="
__MODULE_STRING(SBC7240_MAX_TIMEOUT) ", default="
__MODULE_STRING(SBC7240_TIMEOUT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Disable watchdog when closing device file");
#define SBC7240_OPEN_STATUS_BIT 0
@@ -65,8 +64,7 @@ static void wdt_disable(void)
/* disable the watchdog */
if (test_and_clear_bit(SBC7240_ENABLED_STATUS_BIT, &wdt_status)) {
inb_p(SBC7240_DISABLE_PORT);
- printk(KERN_INFO SBC7240_PREFIX
- "Watchdog timer is now disabled.\n");
+ pr_info("Watchdog timer is now disabled\n");
}
}
@@ -75,23 +73,20 @@ static void wdt_enable(void)
/* enable the watchdog */
if (!test_and_set_bit(SBC7240_ENABLED_STATUS_BIT, &wdt_status)) {
inb_p(SBC7240_ENABLE_PORT);
- printk(KERN_INFO SBC7240_PREFIX
- "Watchdog timer is now enabled.\n");
+ pr_info("Watchdog timer is now enabled\n");
}
}
static int wdt_set_timeout(int t)
{
if (t < 1 || t > SBC7240_MAX_TIMEOUT) {
- printk(KERN_ERR SBC7240_PREFIX
- "timeout value must be 1<=x<=%d\n",
- SBC7240_MAX_TIMEOUT);
+ pr_err("timeout value must be 1<=x<=%d\n", SBC7240_MAX_TIMEOUT);
return -1;
}
/* set the timeout */
outb_p((unsigned)t, SBC7240_SET_TIMEOUT_PORT);
timeout = t;
- printk(KERN_INFO SBC7240_PREFIX "timeout set to %d seconds\n", t);
+ pr_info("timeout set to %d seconds\n", t);
return 0;
}
@@ -150,8 +145,7 @@ static int fop_close(struct inode *inode, struct file *file)
|| !nowayout) {
wdt_disable();
} else {
- printk(KERN_CRIT SBC7240_PREFIX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
wdt_keepalive();
}
@@ -252,7 +246,7 @@ static struct notifier_block wdt_notifier = {
static void __exit sbc7240_wdt_unload(void)
{
- printk(KERN_INFO SBC7240_PREFIX "Removing watchdog\n");
+ pr_info("Removing watchdog\n");
misc_deregister(&wdt_miscdev);
unregister_reboot_notifier(&wdt_notifier);
@@ -264,8 +258,7 @@ static int __init sbc7240_wdt_init(void)
int rc = -EBUSY;
if (!request_region(SBC7240_ENABLE_PORT, 1, "SBC7240 WDT")) {
- printk(KERN_ERR SBC7240_PREFIX
- "I/O address 0x%04x already in use\n",
+ pr_err("I/O address 0x%04x already in use\n",
SBC7240_ENABLE_PORT);
rc = -EIO;
goto err_out;
@@ -277,31 +270,27 @@ static int __init sbc7240_wdt_init(void)
if (timeout < 1 || timeout > SBC7240_MAX_TIMEOUT) {
timeout = SBC7240_TIMEOUT;
- printk(KERN_INFO SBC7240_PREFIX
- "timeout value must be 1<=x<=%d, using %d\n",
- SBC7240_MAX_TIMEOUT, timeout);
+ pr_info("timeout value must be 1<=x<=%d, using %d\n",
+ SBC7240_MAX_TIMEOUT, timeout);
}
wdt_set_timeout(timeout);
wdt_disable();
rc = register_reboot_notifier(&wdt_notifier);
if (rc) {
- printk(KERN_ERR SBC7240_PREFIX
- "cannot register reboot notifier (err=%d)\n", rc);
+ pr_err("cannot register reboot notifier (err=%d)\n", rc);
goto err_out_region;
}
rc = misc_register(&wdt_miscdev);
if (rc) {
- printk(KERN_ERR SBC7240_PREFIX
- "cannot register miscdev on minor=%d (err=%d)\n",
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
wdt_miscdev.minor, rc);
goto err_out_reboot_notifier;
}
- printk(KERN_INFO SBC7240_PREFIX
- "Watchdog driver for SBC7240 initialised (nowayout=%d)\n",
- nowayout);
+ pr_info("Watchdog driver for SBC7240 initialised (nowayout=%d)\n",
+ nowayout);
return 0;
diff --git a/drivers/watchdog/sbc8360.c b/drivers/watchdog/sbc8360.c
index 514ec23..d4781e0 100644
--- a/drivers/watchdog/sbc8360.c
+++ b/drivers/watchdog/sbc8360.c
@@ -36,6 +36,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
@@ -51,13 +53,10 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
static unsigned long sbc8360_is_open;
static char expect_close;
-#define PFX "sbc8360: "
-
/*
*
* Watchdog Timer Configuration
@@ -197,11 +196,11 @@ static int wd_times[64][2] = {
static int timeout = 27;
static int wd_margin = 0xB;
static int wd_multiplier = 2;
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Index into timeout table (0-63) (default=27 (60s))");
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -280,8 +279,7 @@ static int sbc8360_close(struct inode *inode, struct file *file)
if (expect_close == 42)
sbc8360_stop();
else
- printk(KERN_CRIT PFX "SBC8360 device closed unexpectedly. "
- "SBC8360 will not stop!\n");
+ pr_crit("SBC8360 device closed unexpectedly. SBC8360 will not stop!\n");
clear_bit(0, &sbc8360_is_open);
expect_close = 0;
@@ -334,20 +332,19 @@ static int __init sbc8360_init(void)
unsigned long int mseconds = 60000;
if (timeout < 0 || timeout > 63) {
- printk(KERN_ERR PFX "Invalid timeout index (must be 0-63).\n");
+ pr_err("Invalid timeout index (must be 0-63)\n");
res = -EINVAL;
goto out;
}
if (!request_region(SBC8360_ENABLE, 1, "SBC8360")) {
- printk(KERN_ERR PFX "ENABLE method I/O %X is not available.\n",
+ pr_err("ENABLE method I/O %X is not available\n",
SBC8360_ENABLE);
res = -EIO;
goto out;
}
if (!request_region(SBC8360_BASETIME, 1, "SBC8360")) {
- printk(KERN_ERR PFX
- "BASETIME method I/O %X is not available.\n",
+ pr_err("BASETIME method I/O %X is not available\n",
SBC8360_BASETIME);
res = -EIO;
goto out_nobasetimereg;
@@ -355,13 +352,13 @@ static int __init sbc8360_init(void)
res = register_reboot_notifier(&sbc8360_notifier);
if (res) {
- printk(KERN_ERR PFX "Failed to register reboot notifier.\n");
+ pr_err("Failed to register reboot notifier\n");
goto out_noreboot;
}
res = misc_register(&sbc8360_miscdev);
if (res) {
- printk(KERN_ERR PFX "failed to register misc device\n");
+ pr_err("failed to register misc device\n");
goto out_nomisc;
}
@@ -378,7 +375,7 @@ static int __init sbc8360_init(void)
mseconds = (wd_margin + 1) * 100000;
/* My kingdom for the ability to print "0.5 seconds" in the kernel! */
- printk(KERN_INFO PFX "Timeout set at %ld ms.\n", mseconds);
+ pr_info("Timeout set at %ld ms\n", mseconds);
return 0;
diff --git a/drivers/watchdog/sbc_epx_c3.c b/drivers/watchdog/sbc_epx_c3.c
index eaca366..0c3e9f6 100644
--- a/drivers/watchdog/sbc_epx_c3.c
+++ b/drivers/watchdog/sbc_epx_c3.c
@@ -13,6 +13,8 @@
* based on softdog.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -28,13 +30,12 @@
#include <linux/uaccess.h>
#include <linux/io.h>
-#define PFX "epx_c3: "
static int epx_c3_alive;
#define WATCHDOG_TIMEOUT 1 /* 1 sec default timeout */
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -51,7 +52,7 @@ static void epx_c3_stop(void)
outb(0, EPXC3_WATCHDOG_CTL_REG);
- printk(KERN_INFO PFX "Stopped watchdog timer.\n");
+ pr_info("Stopped watchdog timer\n");
}
static void epx_c3_pet(void)
@@ -75,7 +76,7 @@ static int epx_c3_open(struct inode *inode, struct file *file)
epx_c3_pet();
epx_c3_alive = 1;
- printk(KERN_INFO "Started watchdog timer.\n");
+ pr_info("Started watchdog timer\n");
return nonseekable_open(inode, file);
}
@@ -173,9 +174,6 @@ static struct notifier_block epx_c3_notifier = {
.notifier_call = epx_c3_notify_sys,
};
-static const char banner[] __initconst = KERN_INFO PFX
- "Hardware Watchdog Timer for Winsystems EPX-C3 SBC: 0.1\n";
-
static int __init watchdog_init(void)
{
int ret;
@@ -185,20 +183,19 @@ static int __init watchdog_init(void)
ret = register_reboot_notifier(&epx_c3_notifier);
if (ret) {
- printk(KERN_ERR PFX "cannot register reboot notifier "
- "(err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
goto out;
}
ret = misc_register(&epx_c3_miscdev);
if (ret) {
- printk(KERN_ERR PFX "cannot register miscdev on minor=%d "
- "(err=%d)\n", WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
unregister_reboot_notifier(&epx_c3_notifier);
goto out;
}
- printk(banner);
+ pr_info("Hardware Watchdog Timer for Winsystems EPX-C3 SBC: 0.1\n");
return 0;
diff --git a/drivers/watchdog/sbc_fitpc2_wdt.c b/drivers/watchdog/sbc_fitpc2_wdt.c
index d5d3994..90d5527 100644
--- a/drivers/watchdog/sbc_fitpc2_wdt.c
+++ b/drivers/watchdog/sbc_fitpc2_wdt.c
@@ -25,9 +25,8 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static unsigned int margin = 60; /* (secs) Default is 1 minute */
static unsigned long wdt_status;
static DEFINE_MUTEX(wdt_lock);
@@ -171,8 +170,7 @@ static int fitpc2_wdt_release(struct inode *inode, struct file *file)
wdt_disable();
pr_info("Device disabled\n");
} else {
- pr_warning("Device closed unexpectedly -"
- " timer will not stop\n");
+ pr_warn("Device closed unexpectedly - timer will not stop\n");
wdt_enable();
}
@@ -222,8 +220,8 @@ static int __init fitpc2_wdt_init(void)
}
if (margin < 31 || margin > 255) {
- pr_err("margin must be in range 31 - 255"
- " seconds, you tried to set %d\n", margin);
+ pr_err("margin must be in range 31 - 255 seconds, you tried to set %d\n",
+ margin);
err = -EINVAL;
goto err_margin;
}
@@ -231,7 +229,7 @@ static int __init fitpc2_wdt_init(void)
err = misc_register(&fitpc2_wdt_miscdev);
if (err) {
pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, err);
+ WATCHDOG_MINOR, err);
goto err_margin;
}
@@ -261,7 +259,7 @@ MODULE_DESCRIPTION("SBC-FITPC2 Watchdog");
module_param(margin, int, 0);
MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c
index c01daca..3fb83b0 100644
--- a/drivers/watchdog/sc1200wdt.c
+++ b/drivers/watchdog/sc1200wdt.c
@@ -31,6 +31,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/miscdevice.h>
@@ -48,7 +50,6 @@
#define SC1200_MODULE_VER "build 20020303"
#define SC1200_MODULE_NAME "sc1200wdt"
-#define PFX SC1200_MODULE_NAME ": "
#define MAX_TIMEOUT 255 /* 255 minutes */
#define PMIR (io) /* Power Management Index Register */
@@ -71,7 +72,6 @@
#define UART2_IRQ 0x04 /* Serial1 */
/* 5 -7 are reserved */
-static char banner[] __initdata = PFX SC1200_MODULE_VER;
static int timeout = 1;
static int io = -1;
static int io_len = 2; /* for non plug and play */
@@ -93,8 +93,8 @@ MODULE_PARM_DESC(io, "io port");
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -176,7 +176,7 @@ static int sc1200wdt_open(struct inode *inode, struct file *file)
timeout = MAX_TIMEOUT;
sc1200wdt_start();
- printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout);
+ pr_info("Watchdog enabled, timeout = %d min(s)", timeout);
return nonseekable_open(inode, file);
}
@@ -254,11 +254,10 @@ static int sc1200wdt_release(struct inode *inode, struct file *file)
{
if (expect_close == 42) {
sc1200wdt_stop();
- printk(KERN_INFO PFX "Watchdog disabled\n");
+ pr_info("Watchdog disabled\n");
} else {
sc1200wdt_write_data(WDTO, timeout);
- printk(KERN_CRIT PFX
- "Unexpected close!, timeout = %d min(s)\n", timeout);
+ pr_crit("Unexpected close!, timeout = %d min(s)\n", timeout);
}
clear_bit(0, &open_flag);
expect_close = 0;
@@ -361,12 +360,11 @@ static int scl200wdt_pnp_probe(struct pnp_dev *dev,
io_len = pnp_port_len(wdt_dev, 0);
if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
- printk(KERN_ERR PFX "Unable to register IO port %#x\n", io);
+ pr_err("Unable to register IO port %#x\n", io);
return -EBUSY;
}
- printk(KERN_INFO "scl200wdt: PnP device found at io port %#x/%d\n",
- io, io_len);
+ pr_info("PnP device found at io port %#x/%d\n", io, io_len);
return 0;
}
@@ -392,7 +390,7 @@ static int __init sc1200wdt_init(void)
{
int ret;
- printk(KERN_INFO "%s\n", banner);
+ pr_info("%s\n", SC1200_MODULE_VER);
#if defined CONFIG_PNP
if (isapnp) {
@@ -403,7 +401,7 @@ static int __init sc1200wdt_init(void)
#endif
if (io == -1) {
- printk(KERN_ERR PFX "io parameter must be specified\n");
+ pr_err("io parameter must be specified\n");
ret = -EINVAL;
goto out_pnp;
}
@@ -416,7 +414,7 @@ static int __init sc1200wdt_init(void)
#endif
if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
- printk(KERN_ERR PFX "Unable to register IO port %#x\n", io);
+ pr_err("Unable to register IO port %#x\n", io);
ret = -EBUSY;
goto out_pnp;
}
@@ -427,16 +425,14 @@ static int __init sc1200wdt_init(void)
ret = register_reboot_notifier(&sc1200wdt_notifier);
if (ret) {
- printk(KERN_ERR PFX
- "Unable to register reboot notifier err = %d\n", ret);
+ pr_err("Unable to register reboot notifier err = %d\n", ret);
goto out_io;
}
ret = misc_register(&sc1200wdt_miscdev);
if (ret) {
- printk(KERN_ERR PFX
- "Unable to register miscdev on minor %d\n",
- WATCHDOG_MINOR);
+ pr_err("Unable to register miscdev on minor %d\n",
+ WATCHDOG_MINOR);
goto out_rbt;
}
diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c
index b284040..707e027 100644
--- a/drivers/watchdog/sc520_wdt.c
+++ b/drivers/watchdog/sc520_wdt.c
@@ -52,6 +52,8 @@
* This driver uses memory mapped IO, and spinlock.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -67,10 +69,6 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
-
-#define OUR_NAME "sc520_wdt"
-#define PFX OUR_NAME ": "
/*
* The AMD Elan SC520 timeout value is 492us times a power of 2 (0-7)
@@ -98,8 +96,8 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. (1 <= timeout <= 3600, default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -151,8 +149,7 @@ static void wdt_timer_ping(unsigned long data)
/* Re-set the timer interval */
mod_timer(&timer, jiffies + WDT_INTERVAL);
} else
- printk(KERN_WARNING PFX
- "Heartbeat lost! Will not ping the watchdog\n");
+ pr_warn("Heartbeat lost! Will not ping the watchdog\n");
}
/*
@@ -187,7 +184,7 @@ static int wdt_startup(void)
/* Start the watchdog */
wdt_config(WDT_ENB | WDT_WRST_ENB | WDT_EXP_SEL_04);
- printk(KERN_INFO PFX "Watchdog timer is now enabled.\n");
+ pr_info("Watchdog timer is now enabled\n");
return 0;
}
@@ -199,7 +196,7 @@ static int wdt_turnoff(void)
/* Stop the watchdog */
wdt_config(0);
- printk(KERN_INFO PFX "Watchdog timer is now disabled...\n");
+ pr_info("Watchdog timer is now disabled...\n");
return 0;
}
@@ -270,8 +267,7 @@ static int fop_close(struct inode *inode, struct file *file)
if (wdt_expect_close == 42)
wdt_turnoff();
else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
wdt_keepalive();
}
clear_bit(0, &wdt_is_open);
@@ -393,36 +389,32 @@ static int __init sc520_wdt_init(void)
if not reset to the default */
if (wdt_set_heartbeat(timeout)) {
wdt_set_heartbeat(WATCHDOG_TIMEOUT);
- printk(KERN_INFO PFX
- "timeout value must be 1 <= timeout <= 3600, using %d\n",
- WATCHDOG_TIMEOUT);
+ pr_info("timeout value must be 1 <= timeout <= 3600, using %d\n",
+ WATCHDOG_TIMEOUT);
}
wdtmrctl = ioremap(MMCR_BASE + OFFS_WDTMRCTL, 2);
if (!wdtmrctl) {
- printk(KERN_ERR PFX "Unable to remap memory\n");
+ pr_err("Unable to remap memory\n");
rc = -ENOMEM;
goto err_out_region2;
}
rc = register_reboot_notifier(&wdt_notifier);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", rc);
+ pr_err("cannot register reboot notifier (err=%d)\n", rc);
goto err_out_ioremap;
}
rc = misc_register(&wdt_miscdev);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, rc);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, rc);
goto err_out_notifier;
}
- printk(KERN_INFO PFX
- "WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)\n",
- timeout, nowayout);
+ pr_info("WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
return 0;
diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c
index 029467e..bd86f32 100644
--- a/drivers/watchdog/sch311x_wdt.c
+++ b/drivers/watchdog/sch311x_wdt.c
@@ -18,6 +18,8 @@
* Includes, defines, variables, module parameters, ...
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
/* Includes */
#include <linux/module.h> /* For module specific items */
#include <linux/moduleparam.h> /* For new moduleparam's */
@@ -37,7 +39,6 @@
/* Module and version information */
#define DRV_NAME "sch311x_wdt"
-#define PFX DRV_NAME ": "
/* Runtime registers */
#define RESGEN 0x1d
@@ -79,8 +80,8 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. 1<= timeout <=15300, default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -323,8 +324,7 @@ static int sch311x_wdt_close(struct inode *inode, struct file *file)
if (sch311x_wdt_expect_close == 42) {
sch311x_wdt_stop();
} else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
sch311x_wdt_keepalive();
}
clear_bit(0, &sch311x_wdt_is_open);
@@ -504,20 +504,19 @@ static int __init sch311x_detect(int sio_config_port, unsigned short *addr)
/* Check if Logical Device Register is currently active */
if ((sch311x_sio_inb(sio_config_port, 0x30) & 0x01) == 0)
- printk(KERN_INFO PFX "Seems that LDN 0x0a is not active...\n");
+ pr_info("Seems that LDN 0x0a is not active...\n");
/* Get the base address of the runtime registers */
base_addr = (sch311x_sio_inb(sio_config_port, 0x60) << 8) |
sch311x_sio_inb(sio_config_port, 0x61);
if (!base_addr) {
- printk(KERN_ERR PFX "Base address not set.\n");
+ pr_err("Base address not set\n");
err = -ENODEV;
goto exit;
}
*addr = base_addr;
- printk(KERN_INFO PFX "Found an SMSC SCH311%d chip at 0x%04x\n",
- dev_id, base_addr);
+ pr_info("Found an SMSC SCH311%d chip at 0x%04x\n", dev_id, base_addr);
exit:
sch311x_sio_exit(sio_config_port);
diff --git a/drivers/watchdog/scx200_wdt.c b/drivers/watchdog/scx200_wdt.c
index e67b76c..8ae7c28 100644
--- a/drivers/watchdog/scx200_wdt.c
+++ b/drivers/watchdog/scx200_wdt.c
@@ -17,6 +17,8 @@
of any nature resulting due to the use of this software. This
software is provided AS-IS with no warranties. */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -30,7 +32,7 @@
#include <linux/uaccess.h>
#include <linux/io.h>
-#define NAME "scx200_wdt"
+#define DEBUG
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver");
@@ -41,8 +43,8 @@ static int margin = 60; /* in seconds */
module_param(margin, int, 0);
MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
static u16 wdto_restart;
@@ -66,14 +68,13 @@ static void scx200_wdt_ping(void)
static void scx200_wdt_update_margin(void)
{
- printk(KERN_INFO NAME ": timer margin %d seconds\n", margin);
+ pr_info("timer margin %d seconds\n", margin);
wdto_restart = margin * W_SCALE;
}
static void scx200_wdt_enable(void)
{
- printk(KERN_DEBUG NAME ": enabling watchdog timer, wdto_restart = %d\n",
- wdto_restart);
+ pr_debug("enabling watchdog timer, wdto_restart = %d\n", wdto_restart);
spin_lock(&scx_lock);
outw(0, scx200_cb_base + SCx200_WDT_WDTO);
@@ -86,7 +87,7 @@ static void scx200_wdt_enable(void)
static void scx200_wdt_disable(void)
{
- printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
+ pr_debug("disabling watchdog timer\n");
spin_lock(&scx_lock);
outw(0, scx200_cb_base + SCx200_WDT_WDTO);
@@ -108,9 +109,7 @@ static int scx200_wdt_open(struct inode *inode, struct file *file)
static int scx200_wdt_release(struct inode *inode, struct file *file)
{
if (expect_close != 42)
- printk(KERN_WARNING NAME
- ": watchdog device closed unexpectedly, "
- "will not disable the watchdog timer\n");
+ pr_warn("watchdog device closed unexpectedly, will not disable the watchdog timer\n");
else if (!nowayout)
scx200_wdt_disable();
expect_close = 0;
@@ -219,7 +218,7 @@ static int __init scx200_wdt_init(void)
{
int r;
- printk(KERN_DEBUG NAME ": NatSemi SCx200 Watchdog Driver\n");
+ pr_debug("NatSemi SCx200 Watchdog Driver\n");
/* check that we have found the configuration block */
if (!scx200_cb_present())
@@ -228,7 +227,7 @@ static int __init scx200_wdt_init(void)
if (!request_region(scx200_cb_base + SCx200_WDT_OFFSET,
SCx200_WDT_SIZE,
"NatSemi SCx200 Watchdog")) {
- printk(KERN_WARNING NAME ": watchdog I/O region busy\n");
+ pr_warn("watchdog I/O region busy\n");
return -EBUSY;
}
@@ -237,7 +236,7 @@ static int __init scx200_wdt_init(void)
r = register_reboot_notifier(&scx200_wdt_notifier);
if (r) {
- printk(KERN_ERR NAME ": unable to register reboot notifier");
+ pr_err("unable to register reboot notifier\n");
release_region(scx200_cb_base + SCx200_WDT_OFFSET,
SCx200_WDT_SIZE);
return r;
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c
index a267dc0..93958a7 100644
--- a/drivers/watchdog/shwdt.c
+++ b/drivers/watchdog/shwdt.c
@@ -17,6 +17,9 @@
* Added expect close support, made emulated timeout runtime changeable
* general cleanups, add some ioctls
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
@@ -72,7 +75,7 @@ static DEFINE_SPINLOCK(shwdt_lock);
#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */
static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
static unsigned long next_heartbeat;
struct sh_wdt {
@@ -440,20 +443,20 @@ static int __init sh_wdt_init(void)
clock_division_ratio > 0x7)) {
clock_division_ratio = WTCSR_CKS_4096;
- pr_info("%s: divisor must be 0x5<=x<=0x7, using %d\n",
- DRV_NAME, clock_division_ratio);
+ pr_info("divisor must be 0x5<=x<=0x7, using %d\n",
+ clock_division_ratio);
}
rc = sh_wdt_set_heartbeat(heartbeat);
if (unlikely(rc)) {
heartbeat = WATCHDOG_HEARTBEAT;
- pr_info("%s: heartbeat value must be 1<=x<=3600, using %d\n",
- DRV_NAME, heartbeat);
+ pr_info("heartbeat value must be 1<=x<=3600, using %d\n",
+ heartbeat);
}
- pr_info("%s: configured with heartbeat=%d sec (nowayout=%d)\n",
- DRV_NAME, heartbeat, nowayout);
+ pr_info("configured with heartbeat=%d sec (nowayout=%d)\n",
+ heartbeat, nowayout);
return platform_driver_register(&sh_wdt_driver);
}
@@ -481,7 +484,7 @@ MODULE_PARM_DESC(heartbeat,
"Watchdog heartbeat in seconds. (1 <= heartbeat <= 3600, default="
__MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c
index 97b8184..6d665f9 100644
--- a/drivers/watchdog/smsc37b787_wdt.c
+++ b/drivers/watchdog/smsc37b787_wdt.c
@@ -43,6 +43,8 @@
* Documentation/watchdog/wdt.txt
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -58,7 +60,6 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
/* enable support for minutes as units? */
/* (does not always work correctly, so disabled by default!) */
@@ -70,7 +71,6 @@
#define UNIT_SECOND 0
#define UNIT_MINUTE 1
-#define MODNAME "smsc37b787_wdt: "
#define VERSION "1.1"
#define IOPORT 0x3F0
@@ -85,7 +85,7 @@ static char expect_close; /* is the close expected? */
static DEFINE_SPINLOCK(io_lock);/* to guard the watchdog from io races */
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
/* -- Low level function ----------------------------------------*/
@@ -363,8 +363,7 @@ static int wb_smsc_wdt_open(struct inode *inode, struct file *file)
/* Reload and activate timer */
wb_smsc_wdt_enable();
- printk(KERN_INFO MODNAME
- "Watchdog enabled. Timeout set to %d %s.\n",
+ pr_info("Watchdog enabled. Timeout set to %d %s\n",
timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
return nonseekable_open(inode, file);
@@ -378,11 +377,9 @@ static int wb_smsc_wdt_release(struct inode *inode, struct file *file)
if (expect_close == 42) {
wb_smsc_wdt_disable();
- printk(KERN_INFO MODNAME
- "Watchdog disabled, sleeping again...\n");
+ pr_info("Watchdog disabled, sleeping again...\n");
} else {
- printk(KERN_CRIT MODNAME
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
wb_smsc_wdt_reset_timer();
}
@@ -534,12 +531,11 @@ static int __init wb_smsc_wdt_init(void)
{
int ret;
- printk(KERN_INFO "SMsC 37B787 watchdog component driver "
- VERSION " initialising...\n");
+ pr_info("SMsC 37B787 watchdog component driver "
+ VERSION " initialising...\n");
if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) {
- printk(KERN_ERR MODNAME "Unable to register IO port %#x\n",
- IOPORT);
+ pr_err("Unable to register IO port %#x\n", IOPORT);
ret = -EBUSY;
goto out_pnp;
}
@@ -553,25 +549,22 @@ static int __init wb_smsc_wdt_init(void)
ret = register_reboot_notifier(&wb_smsc_wdt_notifier);
if (ret) {
- printk(KERN_ERR MODNAME
- "Unable to register reboot notifier err = %d\n", ret);
+ pr_err("Unable to register reboot notifier err = %d\n", ret);
goto out_io;
}
ret = misc_register(&wb_smsc_wdt_miscdev);
if (ret) {
- printk(KERN_ERR MODNAME
- "Unable to register miscdev on minor %d\n",
- WATCHDOG_MINOR);
+ pr_err("Unable to register miscdev on minor %d\n",
+ WATCHDOG_MINOR);
goto out_rbt;
}
/* output info */
- printk(KERN_INFO MODNAME "Timeout set to %d %s.\n",
+ pr_info("Timeout set to %d %s\n",
timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
- printk(KERN_INFO MODNAME
- "Watchdog initialized and sleeping (nowayout=%d)...\n",
- nowayout);
+ pr_info("Watchdog initialized and sleeping (nowayout=%d)...\n",
+ nowayout);
out_clean:
return ret;
@@ -592,14 +585,14 @@ static void __exit wb_smsc_wdt_exit(void)
/* Stop the timer before we leave */
if (!nowayout) {
wb_smsc_wdt_shutdown();
- printk(KERN_INFO MODNAME "Watchdog disabled.\n");
+ pr_info("Watchdog disabled\n");
}
misc_deregister(&wb_smsc_wdt_miscdev);
unregister_reboot_notifier(&wb_smsc_wdt_notifier);
release_region(IOPORT, IOPORT_SIZE);
- printk(KERN_INFO "SMsC 37B787 watchdog component driver removed.\n");
+ pr_info("SMsC 37B787 watchdog component driver removed\n");
}
module_init(wb_smsc_wdt_init);
@@ -621,7 +614,7 @@ MODULE_PARM_DESC(unit,
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60");
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c
index bf16ffb..fe83beb 100644
--- a/drivers/watchdog/softdog.c
+++ b/drivers/watchdog/softdog.c
@@ -1,5 +1,5 @@
/*
- * SoftDog 0.07: A Software Watchdog Device
+ * SoftDog: A Software Watchdog Device
*
* (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
* All Rights Reserved.
@@ -36,45 +36,37 @@
* Added Matt Domsch's nowayout module option.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
-#include <linux/fs.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/jiffies.h>
-#include <linux/uaccess.h>
#include <linux/kernel.h>
-#define PFX "SoftDog: "
-
#define TIMER_MARGIN 60 /* Default is 60 seconds */
-static int soft_margin = TIMER_MARGIN; /* in seconds */
-module_param(soft_margin, int, 0);
+static unsigned int soft_margin = TIMER_MARGIN; /* in seconds */
+module_param(soft_margin, uint, 0);
MODULE_PARM_DESC(soft_margin,
"Watchdog soft_margin in seconds. (0 < soft_margin < 65536, default="
__MODULE_STRING(TIMER_MARGIN) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-#ifdef ONLY_TESTING
-static int soft_noboot = 1;
-#else
static int soft_noboot = 0;
-#endif /* ONLY_TESTING */
-
module_param(soft_noboot, int, 0);
MODULE_PARM_DESC(soft_noboot,
- "Softdog action, set to 1 to ignore reboots, 0 to reboot "
- "(default depends on ONLY_TESTING)");
+ "Softdog action, set to 1 to ignore reboots, 0 to reboot (default=0)");
static int soft_panic;
module_param(soft_panic, int, 0);
@@ -89,9 +81,6 @@ static void watchdog_fire(unsigned long);
static struct timer_list watchdog_ticktock =
TIMER_INITIALIZER(watchdog_fire, 0, 0);
-static unsigned long driver_open, orphan_timer;
-static char expect_close;
-
/*
* If the timer expires..
@@ -99,18 +88,15 @@ static char expect_close;
static void watchdog_fire(unsigned long data)
{
- if (test_and_clear_bit(0, &orphan_timer))
- module_put(THIS_MODULE);
-
if (soft_noboot)
- printk(KERN_CRIT PFX "Triggered - Reboot ignored.\n");
+ pr_crit("Triggered - Reboot ignored\n");
else if (soft_panic) {
- printk(KERN_CRIT PFX "Initiating panic.\n");
- panic("Software Watchdog Timer expired.");
+ pr_crit("Initiating panic\n");
+ panic("Software Watchdog Timer expired");
} else {
- printk(KERN_CRIT PFX "Initiating system reboot.\n");
+ pr_crit("Initiating system reboot\n");
emergency_restart();
- printk(KERN_CRIT PFX "Reboot didn't ?????\n");
+ pr_crit("Reboot didn't ?????\n");
}
}
@@ -118,127 +104,24 @@ static void watchdog_fire(unsigned long data)
* Softdog operations
*/
-static int softdog_keepalive(void)
+static int softdog_ping(struct watchdog_device *w)
{
- mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
+ mod_timer(&watchdog_ticktock, jiffies+(w->timeout*HZ));
return 0;
}
-static int softdog_stop(void)
+static int softdog_stop(struct watchdog_device *w)
{
del_timer(&watchdog_ticktock);
return 0;
}
-static int softdog_set_heartbeat(int t)
-{
- if ((t < 0x0001) || (t > 0xFFFF))
- return -EINVAL;
-
- soft_margin = t;
- return 0;
-}
-
-/*
- * /dev/watchdog handling
- */
-
-static int softdog_open(struct inode *inode, struct file *file)
-{
- if (test_and_set_bit(0, &driver_open))
- return -EBUSY;
- if (!test_and_clear_bit(0, &orphan_timer))
- __module_get(THIS_MODULE);
- /*
- * Activate timer
- */
- softdog_keepalive();
- return nonseekable_open(inode, file);
-}
-
-static int softdog_release(struct inode *inode, struct file *file)
+static int softdog_set_timeout(struct watchdog_device *w, unsigned int t)
{
- /*
- * Shut off the timer.
- * Lock it in if it's a module and we set nowayout
- */
- if (expect_close == 42) {
- softdog_stop();
- module_put(THIS_MODULE);
- } else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
- set_bit(0, &orphan_timer);
- softdog_keepalive();
- }
- clear_bit(0, &driver_open);
- expect_close = 0;
+ w->timeout = t;
return 0;
}
-static ssize_t softdog_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
-{
- /*
- * Refresh the timer.
- */
- if (len) {
- if (!nowayout) {
- size_t i;
-
- /* In case it was set long ago */
- expect_close = 0;
-
- for (i = 0; i != len; i++) {
- char c;
-
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- expect_close = 42;
- }
- }
- softdog_keepalive();
- }
- return len;
-}
-
-static long softdog_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_margin;
- static const struct watchdog_info ident = {
- .options = WDIOF_SETTIMEOUT |
- WDIOF_KEEPALIVEPING |
- WDIOF_MAGICCLOSE,
- .firmware_version = 0,
- .identity = "Software Watchdog",
- };
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
- case WDIOC_KEEPALIVE:
- softdog_keepalive();
- return 0;
- case WDIOC_SETTIMEOUT:
- if (get_user(new_margin, p))
- return -EFAULT;
- if (softdog_set_heartbeat(new_margin))
- return -EINVAL;
- softdog_keepalive();
- /* Fall */
- case WDIOC_GETTIMEOUT:
- return put_user(soft_margin, p);
- default:
- return -ENOTTY;
- }
-}
-
/*
* Notifier for system down
*/
@@ -248,7 +131,7 @@ static int softdog_notify_sys(struct notifier_block *this, unsigned long code,
{
if (code == SYS_DOWN || code == SYS_HALT)
/* Turn the WDT off */
- softdog_stop();
+ softdog_stop(NULL);
return NOTIFY_DONE;
}
@@ -256,28 +139,29 @@ static int softdog_notify_sys(struct notifier_block *this, unsigned long code,
* Kernel Interfaces
*/
-static const struct file_operations softdog_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = softdog_write,
- .unlocked_ioctl = softdog_ioctl,
- .open = softdog_open,
- .release = softdog_release,
+static struct notifier_block softdog_notifier = {
+ .notifier_call = softdog_notify_sys,
};
-static struct miscdevice softdog_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &softdog_fops,
+static struct watchdog_info softdog_info = {
+ .identity = "Software Watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
};
-static struct notifier_block softdog_notifier = {
- .notifier_call = softdog_notify_sys,
+static struct watchdog_ops softdog_ops = {
+ .owner = THIS_MODULE,
+ .start = softdog_ping,
+ .stop = softdog_stop,
+ .ping = softdog_ping,
+ .set_timeout = softdog_set_timeout,
};
-static char banner[] __initdata = KERN_INFO "Software Watchdog Timer: 0.07 "
- "initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d "
- "(nowayout= %d)\n";
+static struct watchdog_device softdog_dev = {
+ .info = &softdog_info,
+ .ops = &softdog_ops,
+ .min_timeout = 1,
+ .max_timeout = 0xFFFF
+};
static int __init watchdog_init(void)
{
@@ -285,37 +169,36 @@ static int __init watchdog_init(void)
/* Check that the soft_margin value is within it's range;
if not reset to the default */
- if (softdog_set_heartbeat(soft_margin)) {
- softdog_set_heartbeat(TIMER_MARGIN);
- printk(KERN_INFO PFX
- "soft_margin must be 0 < soft_margin < 65536, using %d\n",
+ if (soft_margin < 1 || soft_margin > 65535) {
+ pr_info("soft_margin must be 0 < soft_margin < 65536, using %d\n",
TIMER_MARGIN);
+ return -EINVAL;
}
+ softdog_dev.timeout = soft_margin;
+
+ watchdog_set_nowayout(&softdog_dev, nowayout);
ret = register_reboot_notifier(&softdog_notifier);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
return ret;
}
- ret = misc_register(&softdog_miscdev);
+ ret = watchdog_register_device(&softdog_dev);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
unregister_reboot_notifier(&softdog_notifier);
return ret;
}
- printk(banner, soft_noboot, soft_margin, soft_panic, nowayout);
+ pr_info("Software Watchdog Timer: 0.08 initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n",
+ soft_noboot, soft_margin, soft_panic, nowayout);
return 0;
}
static void __exit watchdog_exit(void)
{
- misc_deregister(&softdog_miscdev);
+ watchdog_unregister_device(&softdog_dev);
unregister_reboot_notifier(&softdog_notifier);
}
diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c
index 87e0527..59108e4 100644
--- a/drivers/watchdog/sp5100_tco.c
+++ b/drivers/watchdog/sp5100_tco.c
@@ -20,6 +20,8 @@
* Includes, defines, variables, module parameters, ...
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -39,7 +41,6 @@
#define TCO_VERSION "0.01"
#define TCO_MODULE_NAME "SP5100 TCO timer"
#define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION
-#define PFX TCO_MODULE_NAME ": "
/* internal variables */
static u32 tcobase_phys;
@@ -61,8 +62,8 @@ module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default="
__MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"
" (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -143,8 +144,7 @@ static int sp5100_tco_release(struct inode *inode, struct file *file)
if (tco_expect_close == 42) {
tco_timer_stop();
} else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
tco_timer_keepalive();
}
clear_bit(0, &timer_alive);
@@ -290,8 +290,7 @@ static unsigned char __devinit sp5100_tco_setupdevice(void)
/* Request the IO ports used by this driver */
pm_iobase = SP5100_IO_PM_INDEX_REG;
if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, "SP5100 TCO")) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
- pm_iobase);
+ pr_err("I/O address 0x%04x already in use\n", pm_iobase);
goto exit;
}
@@ -308,15 +307,14 @@ static unsigned char __devinit sp5100_tco_setupdevice(void)
if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
"SP5100 TCO")) {
- printk(KERN_ERR PFX "mmio address 0x%04x already in use\n",
- val);
+ pr_err("mmio address 0x%04x already in use\n", val);
goto unreg_region;
}
tcobase_phys = val;
tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE);
if (tcobase == 0) {
- printk(KERN_ERR PFX "failed to get tcobase address\n");
+ pr_err("failed to get tcobase address\n");
goto unreg_mem_region;
}
@@ -375,9 +373,9 @@ static int __devinit sp5100_tco_init(struct platform_device *dev)
return -ENODEV;
/* Check to see if last reboot was due to watchdog timeout */
- printk(KERN_INFO PFX "Watchdog reboot %sdetected.\n",
- readl(SP5100_WDT_CONTROL(tcobase)) & SP5100_PM_WATCHDOG_FIRED ?
- "" : "not ");
+ pr_info("Watchdog reboot %sdetected\n",
+ readl(SP5100_WDT_CONTROL(tcobase)) & SP5100_PM_WATCHDOG_FIRED ?
+ "" : "not ");
/* Clear out the old status */
val = readl(SP5100_WDT_CONTROL(tcobase));
@@ -395,16 +393,14 @@ static int __devinit sp5100_tco_init(struct platform_device *dev)
ret = misc_register(&sp5100_tco_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX "cannot register miscdev on minor="
- "%d (err=%d)\n",
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
WATCHDOG_MINOR, ret);
goto exit;
}
clear_bit(0, &timer_alive);
- printk(KERN_INFO PFX "initialized (0x%p). heartbeat=%d sec"
- " (nowayout=%d)\n",
+ pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n",
tcobase, heartbeat, nowayout);
return 0;
@@ -455,8 +451,7 @@ static int __init sp5100_tco_init_module(void)
{
int err;
- printk(KERN_INFO PFX "SP5100 TCO WatchDog Timer Driver v%s\n",
- TCO_VERSION);
+ pr_info("SP5100 TCO WatchDog Timer Driver v%s\n", TCO_VERSION);
err = platform_driver_register(&sp5100_tco_driver);
if (err)
@@ -480,7 +475,7 @@ static void __exit sp5100_tco_cleanup_module(void)
{
platform_device_unregister(sp5100_tco_platform_device);
platform_driver_unregister(&sp5100_tco_driver);
- printk(KERN_INFO PFX "SP5100 TCO Watchdog Module Unloaded.\n");
+ pr_info("SP5100 TCO Watchdog Module Unloaded\n");
}
module_init(sp5100_tco_init_module);
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
index 3ff9e47..bbb170e 100644
--- a/drivers/watchdog/sp805_wdt.c
+++ b/drivers/watchdog/sp805_wdt.c
@@ -25,6 +25,7 @@
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
@@ -55,14 +56,13 @@
/**
* struct sp805_wdt: sp805 wdt device structure
- *
- * lock: spin lock protecting dev structure and io access
- * base: base address of wdt
- * clk: clock structure of wdt
- * dev: amba device structure of wdt
- * status: current status of wdt
- * load_val: load value to be set for current timeout
- * timeout: current programmed timeout
+ * @lock: spin lock protecting dev structure and io access
+ * @base: base address of wdt
+ * @clk: clock structure of wdt
+ * @adev: amba device structure of wdt
+ * @status: current status of wdt
+ * @load_val: load value to be set for current timeout
+ * @timeout: current programmed timeout
*/
struct sp805_wdt {
spinlock_t lock;
@@ -78,7 +78,7 @@ struct sp805_wdt {
/* local variables */
static struct sp805_wdt *wdt;
-static int nowayout = WATCHDOG_NOWAYOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
/* This routine finds load value that will reset system in required timout */
static void wdt_setload(unsigned int timeout)
@@ -113,10 +113,10 @@ static u32 wdt_timeleft(void)
rate = clk_get_rate(wdt->clk);
spin_lock(&wdt->lock);
- load = readl(wdt->base + WDTVALUE);
+ load = readl_relaxed(wdt->base + WDTVALUE);
/*If the interrupt is inactive then time left is WDTValue + WDTLoad. */
- if (!(readl(wdt->base + WDTRIS) & INT_MASK))
+ if (!(readl_relaxed(wdt->base + WDTRIS) & INT_MASK))
load += wdt->load_val + 1;
spin_unlock(&wdt->lock);
@@ -128,14 +128,14 @@ static void wdt_enable(void)
{
spin_lock(&wdt->lock);
- writel(UNLOCK, wdt->base + WDTLOCK);
- writel(wdt->load_val, wdt->base + WDTLOAD);
- writel(INT_MASK, wdt->base + WDTINTCLR);
- writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
- writel(LOCK, wdt->base + WDTLOCK);
+ writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
+ writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
+ writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
+ writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
+ writel_relaxed(LOCK, wdt->base + WDTLOCK);
/* Flush posted writes. */
- readl(wdt->base + WDTLOCK);
+ readl_relaxed(wdt->base + WDTLOCK);
spin_unlock(&wdt->lock);
}
@@ -144,12 +144,12 @@ static void wdt_disable(void)
{
spin_lock(&wdt->lock);
- writel(UNLOCK, wdt->base + WDTLOCK);
- writel(0, wdt->base + WDTCONTROL);
- writel(LOCK, wdt->base + WDTLOCK);
+ writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
+ writel_relaxed(0, wdt->base + WDTCONTROL);
+ writel_relaxed(LOCK, wdt->base + WDTLOCK);
/* Flush posted writes. */
- readl(wdt->base + WDTLOCK);
+ readl_relaxed(wdt->base + WDTLOCK);
spin_unlock(&wdt->lock);
}
@@ -285,32 +285,33 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret = 0;
- if (!request_mem_region(adev->res.start, resource_size(&adev->res),
- "sp805_wdt")) {
+ if (!devm_request_mem_region(&adev->dev, adev->res.start,
+ resource_size(&adev->res), "sp805_wdt")) {
dev_warn(&adev->dev, "Failed to get memory region resource\n");
ret = -ENOENT;
goto err;
}
- wdt = kzalloc(sizeof(*wdt), GFP_KERNEL);
+ wdt = devm_kzalloc(&adev->dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt) {
dev_warn(&adev->dev, "Kzalloc failed\n");
ret = -ENOMEM;
- goto err_kzalloc;
+ goto err;
+ }
+
+ wdt->base = devm_ioremap(&adev->dev, adev->res.start,
+ resource_size(&adev->res));
+ if (!wdt->base) {
+ ret = -ENOMEM;
+ dev_warn(&adev->dev, "ioremap fail\n");
+ goto err;
}
wdt->clk = clk_get(&adev->dev, NULL);
if (IS_ERR(wdt->clk)) {
dev_warn(&adev->dev, "Clock not found\n");
ret = PTR_ERR(wdt->clk);
- goto err_clk_get;
- }
-
- wdt->base = ioremap(adev->res.start, resource_size(&adev->res));
- if (!wdt->base) {
- ret = -ENOMEM;
- dev_warn(&adev->dev, "ioremap fail\n");
- goto err_ioremap;
+ goto err;
}
wdt->adev = adev;
@@ -327,14 +328,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
return 0;
err_misc_register:
- iounmap(wdt->base);
-err_ioremap:
clk_put(wdt->clk);
-err_clk_get:
- kfree(wdt);
- wdt = NULL;
-err_kzalloc:
- release_mem_region(adev->res.start, resource_size(&adev->res));
err:
dev_err(&adev->dev, "Probe Failed!!!\n");
return ret;
@@ -343,14 +337,42 @@ err:
static int __devexit sp805_wdt_remove(struct amba_device *adev)
{
misc_deregister(&sp805_wdt_miscdev);
- iounmap(wdt->base);
clk_put(wdt->clk);
- kfree(wdt);
- release_mem_region(adev->res.start, resource_size(&adev->res));
return 0;
}
+#ifdef CONFIG_PM
+static int sp805_wdt_suspend(struct device *dev)
+{
+ if (test_bit(WDT_BUSY, &wdt->status)) {
+ wdt_disable();
+ clk_disable(wdt->clk);
+ }
+
+ return 0;
+}
+
+static int sp805_wdt_resume(struct device *dev)
+{
+ int ret = 0;
+
+ if (test_bit(WDT_BUSY, &wdt->status)) {
+ ret = clk_enable(wdt->clk);
+ if (ret) {
+ dev_err(dev, "clock enable fail");
+ return ret;
+ }
+ wdt_enable();
+ }
+
+ return ret;
+}
+#endif /* CONFIG_PM */
+
+static SIMPLE_DEV_PM_OPS(sp805_wdt_dev_pm_ops, sp805_wdt_suspend,
+ sp805_wdt_resume);
+
static struct amba_id sp805_wdt_ids[] = {
{
.id = 0x00141805,
@@ -364,6 +386,7 @@ MODULE_DEVICE_TABLE(amba, sp805_wdt_ids);
static struct amba_driver sp805_wdt_driver = {
.drv = {
.name = MODULE_NAME,
+ .pm = &sp805_wdt_dev_pm_ops,
},
.id_table = sp805_wdt_ids,
.probe = sp805_wdt_probe,
@@ -372,7 +395,7 @@ static struct amba_driver sp805_wdt_driver = {
module_amba_driver(sp805_wdt_driver);
-module_param(nowayout, int, 0);
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Set to 1 to keep watchdog running after device release");
diff --git a/drivers/watchdog/stmp3xxx_wdt.c b/drivers/watchdog/stmp3xxx_wdt.c
index e37d811..21d96b9 100644
--- a/drivers/watchdog/stmp3xxx_wdt.c
+++ b/drivers/watchdog/stmp3xxx_wdt.c
@@ -6,6 +6,9 @@
* Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
@@ -32,7 +35,7 @@
static DEFINE_SPINLOCK(stmp3xxx_wdt_io_lock);
static unsigned long wdt_status;
-static const int nowayout = WATCHDOG_NOWAYOUT;
+static const bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = DEFAULT_HEARTBEAT;
static unsigned long boot_status;
@@ -221,8 +224,7 @@ static int __devinit stmp3xxx_wdt_probe(struct platform_device *pdev)
return ret;
}
- printk(KERN_INFO "stmp3xxx watchdog: initialized, heartbeat %d sec\n",
- heartbeat);
+ pr_info("initialized, heartbeat %d sec\n", heartbeat);
return ret;
}
diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c
index 1490293..8df050d 100644
--- a/drivers/watchdog/ts72xx_wdt.c
+++ b/drivers/watchdog/ts72xx_wdt.c
@@ -34,8 +34,8 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. "
__MODULE_STRING(TS72XX_WDT_DEFAULT_TIMEOUT)
")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
/**
diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c
index 0764c62..249f113 100644
--- a/drivers/watchdog/twl4030_wdt.c
+++ b/drivers/watchdog/twl4030_wdt.c
@@ -42,8 +42,8 @@ struct twl4030_wdt {
unsigned long state;
};
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
diff --git a/drivers/watchdog/txx9wdt.c b/drivers/watchdog/txx9wdt.c
index 9e9ed7b..98e1637 100644
--- a/drivers/watchdog/txx9wdt.c
+++ b/drivers/watchdog/txx9wdt.c
@@ -7,177 +7,99 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
-#include <linux/fs.h>
#include <linux/init.h>
-#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <asm/txx9tmr.h>
+#define WD_TIMER_CCD 7 /* 1/256 */
+#define WD_TIMER_CLK (clk_get_rate(txx9_imclk) / (2 << WD_TIMER_CCD))
+#define WD_MAX_TIMEOUT ((0xffffffff >> (32 - TXX9_TIMER_BITS)) / WD_TIMER_CLK)
#define TIMER_MARGIN 60 /* Default is 60 seconds */
-static int timeout = TIMER_MARGIN; /* in seconds */
-module_param(timeout, int, 0);
+static unsigned int timeout = TIMER_MARGIN; /* in seconds */
+module_param(timeout, uint, 0);
MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. "
"(0<timeout<((2^" __MODULE_STRING(TXX9_TIMER_BITS) ")/(IMCLK/256)), "
"default=" __MODULE_STRING(TIMER_MARGIN) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-#define WD_TIMER_CCD 7 /* 1/256 */
-#define WD_TIMER_CLK (clk_get_rate(txx9_imclk) / (2 << WD_TIMER_CCD))
-#define WD_MAX_TIMEOUT ((0xffffffff >> (32 - TXX9_TIMER_BITS)) / WD_TIMER_CLK)
-
-static unsigned long txx9wdt_alive;
-static int expect_close;
static struct txx9_tmr_reg __iomem *txx9wdt_reg;
static struct clk *txx9_imclk;
static DEFINE_SPINLOCK(txx9_lock);
-static void txx9wdt_ping(void)
+static int txx9wdt_ping(struct watchdog_device *wdt_dev)
{
spin_lock(&txx9_lock);
__raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr);
spin_unlock(&txx9_lock);
+ return 0;
}
-static void txx9wdt_start(void)
+static int txx9wdt_start(struct watchdog_device *wdt_dev)
{
spin_lock(&txx9_lock);
- __raw_writel(WD_TIMER_CLK * timeout, &txx9wdt_reg->cpra);
+ __raw_writel(WD_TIMER_CLK * wdt_dev->timeout, &txx9wdt_reg->cpra);
__raw_writel(WD_TIMER_CCD, &txx9wdt_reg->ccdr);
__raw_writel(0, &txx9wdt_reg->tisr); /* clear pending interrupt */
__raw_writel(TXx9_TMTCR_TCE | TXx9_TMTCR_CCDE | TXx9_TMTCR_TMODE_WDOG,
&txx9wdt_reg->tcr);
__raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr);
spin_unlock(&txx9_lock);
+ return 0;
}
-static void txx9wdt_stop(void)
+static int txx9wdt_stop(struct watchdog_device *wdt_dev)
{
spin_lock(&txx9_lock);
__raw_writel(TXx9_TMWTMR_WDIS, &txx9wdt_reg->wtmr);
__raw_writel(__raw_readl(&txx9wdt_reg->tcr) & ~TXx9_TMTCR_TCE,
&txx9wdt_reg->tcr);
spin_unlock(&txx9_lock);
-}
-
-static int txx9wdt_open(struct inode *inode, struct file *file)
-{
- if (test_and_set_bit(0, &txx9wdt_alive))
- return -EBUSY;
-
- if (__raw_readl(&txx9wdt_reg->tcr) & TXx9_TMTCR_TCE) {
- clear_bit(0, &txx9wdt_alive);
- return -EBUSY;
- }
-
- if (nowayout)
- __module_get(THIS_MODULE);
-
- txx9wdt_start();
- return nonseekable_open(inode, file);
-}
-
-static int txx9wdt_release(struct inode *inode, struct file *file)
-{
- if (expect_close)
- txx9wdt_stop();
- else {
- printk(KERN_CRIT "txx9wdt: "
- "Unexpected close, not stopping watchdog!\n");
- txx9wdt_ping();
- }
- clear_bit(0, &txx9wdt_alive);
- expect_close = 0;
return 0;
}
-static ssize_t txx9wdt_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
+static int txx9wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int new_timeout)
{
- if (len) {
- if (!nowayout) {
- size_t i;
-
- expect_close = 0;
- for (i = 0; i != len; i++) {
- char c;
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- expect_close = 1;
- }
- }
- txx9wdt_ping();
- }
- return len;
+ wdt_dev->timeout = new_timeout;
+ txx9wdt_stop(wdt_dev);
+ txx9wdt_start(wdt_dev);
+ return 0;
}
-static long txx9wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_timeout;
- static const struct watchdog_info ident = {
- .options = WDIOF_SETTIMEOUT |
- WDIOF_KEEPALIVEPING |
- WDIOF_MAGICCLOSE,
- .firmware_version = 0,
- .identity = "Hardware Watchdog for TXx9",
- };
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
- case WDIOC_KEEPALIVE:
- txx9wdt_ping();
- return 0;
- case WDIOC_SETTIMEOUT:
- if (get_user(new_timeout, p))
- return -EFAULT;
- if (new_timeout < 1 || new_timeout > WD_MAX_TIMEOUT)
- return -EINVAL;
- timeout = new_timeout;
- txx9wdt_stop();
- txx9wdt_start();
- /* Fall */
- case WDIOC_GETTIMEOUT:
- return put_user(timeout, p);
- default:
- return -ENOTTY;
- }
-}
+static const struct watchdog_info txx9wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "Hardware Watchdog for TXx9",
+};
-static const struct file_operations txx9wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = txx9wdt_write,
- .unlocked_ioctl = txx9wdt_ioctl,
- .open = txx9wdt_open,
- .release = txx9wdt_release,
+static const struct watchdog_ops txx9wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = txx9wdt_start,
+ .stop = txx9wdt_stop,
+ .ping = txx9wdt_ping,
+ .set_timeout = txx9wdt_set_timeout,
};
-static struct miscdevice txx9wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &txx9wdt_fops,
+static struct watchdog_device txx9wdt = {
+ .info = &txx9wdt_info,
+ .ops = &txx9wdt_ops,
};
static int __init txx9wdt_probe(struct platform_device *dev)
@@ -199,27 +121,27 @@ static int __init txx9wdt_probe(struct platform_device *dev)
}
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!res)
- goto exit_busy;
- if (!devm_request_mem_region(&dev->dev, res->start, resource_size(res),
- "txx9wdt"))
- goto exit_busy;
- txx9wdt_reg = devm_ioremap(&dev->dev, res->start, resource_size(res));
- if (!txx9wdt_reg)
- goto exit_busy;
-
- ret = misc_register(&txx9wdt_miscdev);
- if (ret) {
+ txx9wdt_reg = devm_request_and_ioremap(&dev->dev, res);
+ if (!txx9wdt_reg) {
+ ret = -EBUSY;
goto exit;
}
- printk(KERN_INFO "Hardware Watchdog Timer for TXx9: "
- "timeout=%d sec (max %ld) (nowayout= %d)\n",
- timeout, WD_MAX_TIMEOUT, nowayout);
+ if (timeout < 1 || timeout > WD_MAX_TIMEOUT)
+ timeout = TIMER_MARGIN;
+ txx9wdt.timeout = timeout;
+ txx9wdt.min_timeout = 1;
+ txx9wdt.max_timeout = WD_MAX_TIMEOUT;
+ watchdog_set_nowayout(&txx9wdt, nowayout);
+
+ ret = watchdog_register_device(&txx9wdt);
+ if (ret)
+ goto exit;
+
+ pr_info("Hardware Watchdog Timer: timeout=%d sec (max %ld) (nowayout= %d)\n",
+ timeout, WD_MAX_TIMEOUT, nowayout);
return 0;
-exit_busy:
- ret = -EBUSY;
exit:
if (txx9_imclk) {
clk_disable(txx9_imclk);
@@ -230,7 +152,7 @@ exit:
static int __exit txx9wdt_remove(struct platform_device *dev)
{
- misc_deregister(&txx9wdt_miscdev);
+ watchdog_unregister_device(&txx9wdt);
clk_disable(txx9_imclk);
clk_put(txx9_imclk);
return 0;
@@ -238,7 +160,7 @@ static int __exit txx9wdt_remove(struct platform_device *dev)
static void txx9wdt_shutdown(struct platform_device *dev)
{
- txx9wdt_stop();
+ txx9wdt_stop(&txx9wdt);
}
static struct platform_driver txx9wdt_driver = {
diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c
index 8f07dd4..465e082 100644
--- a/drivers/watchdog/via_wdt.c
+++ b/drivers/watchdog/via_wdt.c
@@ -10,6 +10,9 @@
* Caveat: PnP must be enabled in BIOS to allow full access to watchdog
* control registers. If not, the watchdog must be configured in BIOS manually.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/device.h>
#include <linux/io.h>
#include <linux/jiffies.h>
@@ -55,8 +58,8 @@ module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, between 1 and 1023 "
"(default = " __MODULE_STRING(WDT_TIMEOUT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default = " __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -98,7 +101,7 @@ static void wdt_timer_tick(unsigned long data)
static int wdt_ping(struct watchdog_device *wdd)
{
/* calculate when the next userspace timeout will be */
- next_heartbeat = jiffies + timeout * HZ;
+ next_heartbeat = jiffies + wdd->timeout * HZ;
return 0;
}
@@ -106,7 +109,7 @@ static int wdt_start(struct watchdog_device *wdd)
{
unsigned int ctl = readl(wdt_mem);
- writel(timeout, wdt_mem + VIA_WDT_COUNT);
+ writel(wdd->timeout, wdt_mem + VIA_WDT_COUNT);
writel(ctl | VIA_WDT_RUNNING | VIA_WDT_TRIGGER, wdt_mem);
wdt_ping(wdd);
mod_timer(&timer, jiffies + WDT_HEARTBEAT);
@@ -125,7 +128,7 @@ static int wdt_set_timeout(struct watchdog_device *wdd,
unsigned int new_timeout)
{
writel(new_timeout, wdt_mem + VIA_WDT_COUNT);
- timeout = new_timeout;
+ wdd->timeout = new_timeout;
return 0;
}
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
index 576a388..92f1326 100644
--- a/drivers/watchdog/w83627hf_wdt.c
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -26,6 +26,8 @@
* (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -40,10 +42,8 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"
-#define PFX WATCHDOG_NAME ": "
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
static unsigned long wdt_is_open;
@@ -61,8 +61,8 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. 1 <= timeout <= 255, default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -119,9 +119,8 @@ static void w83627hf_init(void)
outb_p(0xF6, WDT_EFER); /* Select CRF6 */
t = inb_p(WDT_EFDR); /* read CRF6 */
if (t != 0) {
- printk(KERN_INFO PFX
- "Watchdog already running. Resetting timeout to %d sec\n",
- timeout);
+ pr_info("Watchdog already running. Resetting timeout to %d sec\n",
+ timeout);
outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
}
@@ -290,8 +289,7 @@ static int wdt_close(struct inode *inode, struct file *file)
if (expect_close == 42)
wdt_disable();
else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
wdt_ping();
}
expect_close = 0;
@@ -344,18 +342,16 @@ static int __init wdt_init(void)
{
int ret;
- printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising.\n");
+ pr_info("WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising\n");
if (wdt_set_heartbeat(timeout)) {
wdt_set_heartbeat(WATCHDOG_TIMEOUT);
- printk(KERN_INFO PFX
- "timeout value must be 1 <= timeout <= 255, using %d\n",
- WATCHDOG_TIMEOUT);
+ pr_info("timeout value must be 1 <= timeout <= 255, using %d\n",
+ WATCHDOG_TIMEOUT);
}
if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
- wdt_io);
+ pr_err("I/O address 0x%04x already in use\n", wdt_io);
ret = -EIO;
goto out;
}
@@ -364,22 +360,19 @@ static int __init wdt_init(void)
ret = register_reboot_notifier(&wdt_notifier);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
goto unreg_regions;
}
ret = misc_register(&wdt_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto unreg_reboot;
}
- printk(KERN_INFO PFX
- "initialized. timeout=%d sec (nowayout=%d)\n",
- timeout, nowayout);
+ pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
out:
return ret;
diff --git a/drivers/watchdog/w83697hf_wdt.c b/drivers/watchdog/w83697hf_wdt.c
index af08972..cd9f3c1 100644
--- a/drivers/watchdog/w83697hf_wdt.c
+++ b/drivers/watchdog/w83697hf_wdt.c
@@ -25,6 +25,8 @@
* "AS-IS" and at no charge.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -39,10 +41,8 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#define WATCHDOG_NAME "w83697hf/hg WDT"
-#define PFX WATCHDOG_NAME ": "
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
#define WATCHDOG_EARLY_DISABLE 1 /* Disable until userland kicks in */
@@ -62,8 +62,8 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. 1<= timeout <=255 (default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -309,8 +309,7 @@ static int wdt_close(struct inode *inode, struct file *file)
if (expect_close == 42)
wdt_disable();
else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
wdt_ping();
}
expect_close = 0;
@@ -362,24 +361,21 @@ static struct notifier_block wdt_notifier = {
static int w83697hf_check_wdt(void)
{
if (!request_region(wdt_io, 2, WATCHDOG_NAME)) {
- printk(KERN_ERR PFX
- "I/O address 0x%x already in use\n", wdt_io);
+ pr_err("I/O address 0x%x already in use\n", wdt_io);
return -EIO;
}
- printk(KERN_DEBUG PFX
- "Looking for watchdog at address 0x%x\n", wdt_io);
+ pr_debug("Looking for watchdog at address 0x%x\n", wdt_io);
w83697hf_unlock();
if (w83697hf_get_reg(0x20) == 0x60) {
- printk(KERN_INFO PFX
- "watchdog found at address 0x%x\n", wdt_io);
+ pr_info("watchdog found at address 0x%x\n", wdt_io);
w83697hf_lock();
return 0;
}
/* Reprotect in case it was a compatible device */
w83697hf_lock();
- printk(KERN_INFO PFX "watchdog not found at address 0x%x\n", wdt_io);
+ pr_info("watchdog not found at address 0x%x\n", wdt_io);
release_region(wdt_io, 2);
return -EIO;
}
@@ -390,7 +386,7 @@ static int __init wdt_init(void)
{
int ret, i, found = 0;
- printk(KERN_INFO PFX "WDT driver for W83697HF/HG initializing\n");
+ pr_info("WDT driver for W83697HF/HG initializing\n");
if (wdt_io == 0) {
/* we will autodetect the W83697HF/HG watchdog */
@@ -405,7 +401,7 @@ static int __init wdt_init(void)
}
if (!found) {
- printk(KERN_ERR PFX "No W83697HF/HG could be found\n");
+ pr_err("No W83697HF/HG could be found\n");
ret = -EIO;
goto out;
}
@@ -413,34 +409,30 @@ static int __init wdt_init(void)
w83697hf_init();
if (early_disable) {
if (wdt_running())
- printk(KERN_WARNING PFX "Stopping previously enabled "
- "watchdog until userland kicks in\n");
+ pr_warn("Stopping previously enabled watchdog until userland kicks in\n");
wdt_disable();
}
if (wdt_set_heartbeat(timeout)) {
wdt_set_heartbeat(WATCHDOG_TIMEOUT);
- printk(KERN_INFO PFX
- "timeout value must be 1 <= timeout <= 255, using %d\n",
- WATCHDOG_TIMEOUT);
+ pr_info("timeout value must be 1 <= timeout <= 255, using %d\n",
+ WATCHDOG_TIMEOUT);
}
ret = register_reboot_notifier(&wdt_notifier);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
goto unreg_regions;
}
ret = misc_register(&wdt_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto unreg_reboot;
}
- printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
+ pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
timeout, nowayout);
out:
diff --git a/drivers/watchdog/w83697ug_wdt.c b/drivers/watchdog/w83697ug_wdt.c
index be9c4d8..274be0b 100644
--- a/drivers/watchdog/w83697ug_wdt.c
+++ b/drivers/watchdog/w83697ug_wdt.c
@@ -30,6 +30,8 @@
* (c) Copyright 1995 Alan Cox <alan@redhat.com>
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -44,10 +46,8 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#define WATCHDOG_NAME "w83697ug/uf WDT"
-#define PFX WATCHDOG_NAME ": "
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
static unsigned long wdt_is_open;
@@ -64,8 +64,8 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. 1<= timeout <=255 (default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -91,8 +91,8 @@ static int w83697ug_select_wd_register(void)
version = inb(WDT_EFDR);
if (version == 0x68) { /* W83697UG */
- printk(KERN_INFO PFX "Watchdog chip version 0x%02x = "
- "W83697UG/UF found at 0x%04x\n", version, wdt_io);
+ pr_info("Watchdog chip version 0x%02x = W83697UG/UF found at 0x%04x\n",
+ version, wdt_io);
outb_p(0x2b, WDT_EFER);
c = inb_p(WDT_EFDR); /* select WDT0 */
@@ -101,7 +101,7 @@ static int w83697ug_select_wd_register(void)
outb_p(c, WDT_EFDR); /* set pin118 to WDT0 */
} else {
- printk(KERN_ERR PFX "No W83697UG/UF could be found\n");
+ pr_err("No W83697UG/UF could be found\n");
return -ENODEV;
}
@@ -131,8 +131,8 @@ static int w83697ug_init(void)
outb_p(0xF6, WDT_EFER); /* Select CRF6 */
t = inb_p(WDT_EFDR); /* read CRF6 */
if (t != 0) {
- printk(KERN_INFO PFX "Watchdog already running."
- " Resetting timeout to %d sec\n", timeout);
+ pr_info("Watchdog already running. Resetting timeout to %d sec\n",
+ timeout);
outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
}
outb_p(0xF5, WDT_EFER); /* Select CRF5 */
@@ -286,8 +286,7 @@ static int wdt_close(struct inode *inode, struct file *file)
if (expect_close == 42)
wdt_disable();
else {
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
wdt_ping();
}
expect_close = 0;
@@ -340,18 +339,16 @@ static int __init wdt_init(void)
{
int ret;
- printk(KERN_INFO "WDT driver for the Winbond(TM) W83697UG/UF Super I/O chip initialising.\n");
+ pr_info("WDT driver for the Winbond(TM) W83697UG/UF Super I/O chip initialising\n");
if (wdt_set_heartbeat(timeout)) {
wdt_set_heartbeat(WATCHDOG_TIMEOUT);
- printk(KERN_INFO PFX
- "timeout value must be 1<=timeout<=255, using %d\n",
+ pr_info("timeout value must be 1<=timeout<=255, using %d\n",
WATCHDOG_TIMEOUT);
}
if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
- wdt_io);
+ pr_err("I/O address 0x%04x already in use\n", wdt_io);
ret = -EIO;
goto out;
}
@@ -362,20 +359,18 @@ static int __init wdt_init(void)
ret = register_reboot_notifier(&wdt_notifier);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
goto unreg_regions;
}
ret = misc_register(&wdt_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto unreg_reboot;
}
- printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
+ pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
timeout, nowayout);
out:
diff --git a/drivers/watchdog/w83877f_wdt.c b/drivers/watchdog/w83877f_wdt.c
index 24587d2..7874ae0 100644
--- a/drivers/watchdog/w83877f_wdt.c
+++ b/drivers/watchdog/w83877f_wdt.c
@@ -42,6 +42,8 @@
* daemon always getting scheduled within that time frame.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -56,10 +58,8 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#define OUR_NAME "w83877f_wdt"
-#define PFX OUR_NAME ": "
#define ENABLE_W83877F_PORT 0x3F0
#define ENABLE_W83877F 0x87
@@ -91,8 +91,8 @@ MODULE_PARM_DESC(timeout,
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -126,8 +126,7 @@ static void wdt_timer_ping(unsigned long data)
spin_unlock(&wdt_spinlock);
} else
- printk(KERN_WARNING PFX
- "Heartbeat lost! Will not ping the watchdog\n");
+ pr_warn("Heartbeat lost! Will not ping the watchdog\n");
}
/*
@@ -165,7 +164,7 @@ static void wdt_startup(void)
wdt_change(WDT_ENABLE);
- printk(KERN_INFO PFX "Watchdog timer is now enabled.\n");
+ pr_info("Watchdog timer is now enabled\n");
}
static void wdt_turnoff(void)
@@ -175,7 +174,7 @@ static void wdt_turnoff(void)
wdt_change(WDT_DISABLE);
- printk(KERN_INFO PFX "Watchdog timer is now disabled...\n");
+ pr_info("Watchdog timer is now disabled...\n");
}
static void wdt_keepalive(void)
@@ -234,8 +233,7 @@ static int fop_close(struct inode *inode, struct file *file)
wdt_turnoff();
else {
del_timer(&timer);
- printk(KERN_CRIT PFX
- "device file closed unexpectedly. Will not stop the WDT!\n");
+ pr_crit("device file closed unexpectedly. Will not stop the WDT!\n");
}
clear_bit(0, &wdt_is_open);
wdt_expect_close = 0;
@@ -357,42 +355,37 @@ static int __init w83877f_wdt_init(void)
if (timeout < 1 || timeout > 3600) { /* arbitrary upper limit */
timeout = WATCHDOG_TIMEOUT;
- printk(KERN_INFO PFX
- "timeout value must be 1 <= x <= 3600, using %d\n",
- timeout);
+ pr_info("timeout value must be 1 <= x <= 3600, using %d\n",
+ timeout);
}
if (!request_region(ENABLE_W83877F_PORT, 2, "W83877F WDT")) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
- ENABLE_W83877F_PORT);
+ pr_err("I/O address 0x%04x already in use\n",
+ ENABLE_W83877F_PORT);
rc = -EIO;
goto err_out;
}
if (!request_region(WDT_PING, 1, "W8387FF WDT")) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
- WDT_PING);
+ pr_err("I/O address 0x%04x already in use\n", WDT_PING);
rc = -EIO;
goto err_out_region1;
}
rc = register_reboot_notifier(&wdt_notifier);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", rc);
+ pr_err("cannot register reboot notifier (err=%d)\n", rc);
goto err_out_region2;
}
rc = misc_register(&wdt_miscdev);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- wdt_miscdev.minor, rc);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
goto err_out_reboot;
}
- printk(KERN_INFO PFX
- "WDT driver for W83877F initialised. timeout=%d sec (nowayout=%d)\n",
+ pr_info("WDT driver for W83877F initialised. timeout=%d sec (nowayout=%d)\n",
timeout, nowayout);
return 0;
diff --git a/drivers/watchdog/w83977f_wdt.c b/drivers/watchdog/w83977f_wdt.c
index 6e6743d..5d2c902 100644
--- a/drivers/watchdog/w83977f_wdt.c
+++ b/drivers/watchdog/w83977f_wdt.c
@@ -15,6 +15,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -29,12 +31,9 @@
#include <linux/uaccess.h>
#include <linux/io.h>
-#include <asm/system.h>
#define WATCHDOG_VERSION "1.00"
#define WATCHDOG_NAME "W83977F WDT"
-#define PFX WATCHDOG_NAME ": "
-#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
#define IO_INDEX_PORT 0x3F0
#define IO_DATA_PORT (IO_INDEX_PORT+1)
@@ -59,8 +58,8 @@ MODULE_PARM_DESC(timeout,
module_param(testmode, int, 0);
MODULE_PARM_DESC(testmode, "Watchdog testmode (1 = no reboot), default=0");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -131,7 +130,7 @@ static int wdt_start(void)
spin_unlock_irqrestore(&spinlock, flags);
- printk(KERN_INFO PFX "activated.\n");
+ pr_info("activated\n");
return 0;
}
@@ -185,7 +184,7 @@ static int wdt_stop(void)
spin_unlock_irqrestore(&spinlock, flags);
- printk(KERN_INFO PFX "shutdown.\n");
+ pr_info("shutdown\n");
return 0;
}
@@ -313,8 +312,7 @@ static int wdt_release(struct inode *inode, struct file *file)
clear_bit(0, &timer_alive);
} else {
wdt_keepalive();
- printk(KERN_CRIT PFX
- "unexpected close, not stopping watchdog!\n");
+ pr_crit("unexpected close, not stopping watchdog!\n");
}
expect_close = 0;
return 0;
@@ -471,7 +469,7 @@ static int __init w83977f_wdt_init(void)
{
int rc;
- printk(KERN_INFO PFX DRIVER_VERSION);
+ pr_info("driver v%s\n", WATCHDOG_VERSION);
/*
* Check that the timeout value is within it's range;
@@ -479,36 +477,31 @@ static int __init w83977f_wdt_init(void)
*/
if (wdt_set_timeout(timeout)) {
wdt_set_timeout(DEFAULT_TIMEOUT);
- printk(KERN_INFO PFX
- "timeout value must be 15 <= timeout <= 7635, using %d\n",
- DEFAULT_TIMEOUT);
+ pr_info("timeout value must be 15 <= timeout <= 7635, using %d\n",
+ DEFAULT_TIMEOUT);
}
if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
- IO_INDEX_PORT);
+ pr_err("I/O address 0x%04x already in use\n", IO_INDEX_PORT);
rc = -EIO;
goto err_out;
}
rc = register_reboot_notifier(&wdt_notifier);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", rc);
+ pr_err("cannot register reboot notifier (err=%d)\n", rc);
goto err_out_region;
}
rc = misc_register(&wdt_miscdev);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- wdt_miscdev.minor, rc);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
goto err_out_reboot;
}
- printk(KERN_INFO PFX
- "initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
- timeout, nowayout, testmode);
+ pr_info("initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
+ timeout, nowayout, testmode);
return 0;
diff --git a/drivers/watchdog/wafer5823wdt.c b/drivers/watchdog/wafer5823wdt.c
index c3c3188..25aba6e 100644
--- a/drivers/watchdog/wafer5823wdt.c
+++ b/drivers/watchdog/wafer5823wdt.c
@@ -26,6 +26,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/miscdevice.h>
@@ -65,8 +67,8 @@ MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. 1 <= timeout <= 255, default="
__MODULE_STRING(WD_TIMO) ".");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -203,8 +205,7 @@ static int wafwdt_close(struct inode *inode, struct file *file)
if (expect_close == 42)
wafwdt_stop();
else {
- printk(KERN_CRIT PFX
- "WDT device closed unexpectedly. WDT will not stop!\n");
+ pr_crit("WDT device closed unexpectedly. WDT will not stop!\n");
wafwdt_ping();
}
clear_bit(0, &wafwdt_is_open);
@@ -256,49 +257,42 @@ static int __init wafwdt_init(void)
{
int ret;
- printk(KERN_INFO
- "WDT driver for Wafer 5823 single board computer initialising.\n");
+ pr_info("WDT driver for Wafer 5823 single board computer initialising\n");
if (timeout < 1 || timeout > 255) {
timeout = WD_TIMO;
- printk(KERN_INFO PFX
- "timeout value must be 1 <= x <= 255, using %d\n",
- timeout);
+ pr_info("timeout value must be 1 <= x <= 255, using %d\n",
+ timeout);
}
if (wdt_stop != wdt_start) {
if (!request_region(wdt_stop, 1, "Wafer 5823 WDT")) {
- printk(KERN_ERR PFX
- "I/O address 0x%04x already in use\n",
- wdt_stop);
+ pr_err("I/O address 0x%04x already in use\n", wdt_stop);
ret = -EIO;
goto error;
}
}
if (!request_region(wdt_start, 1, "Wafer 5823 WDT")) {
- printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
- wdt_start);
+ pr_err("I/O address 0x%04x already in use\n", wdt_start);
ret = -EIO;
goto error2;
}
ret = register_reboot_notifier(&wafwdt_notifier);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
goto error3;
}
ret = misc_register(&wafwdt_miscdev);
if (ret != 0) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto error4;
}
- printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
+ pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
timeout, nowayout);
return ret;
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index cfa1a15..14d768b 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -77,7 +77,7 @@ int watchdog_register_device(struct watchdog_device *wdd)
/* We only support 1 watchdog device via the /dev/watchdog interface */
ret = watchdog_dev_register(wdd);
if (ret) {
- pr_err("error registering /dev/watchdog (err=%d).\n", ret);
+ pr_err("error registering /dev/watchdog (err=%d)\n", ret);
return ret;
}
@@ -101,7 +101,7 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
ret = watchdog_dev_unregister(wdd);
if (ret)
- pr_err("error unregistering /dev/watchdog (err=%d).\n", ret);
+ pr_err("error unregistering /dev/watchdog (err=%d)\n", ret);
}
EXPORT_SYMBOL_GPL(watchdog_unregister_device);
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 1199da0..8558da9 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -226,7 +226,6 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
err = wdd->ops->set_timeout(wdd, val);
if (err < 0)
return err;
- wdd->timeout = val;
/* If the watchdog is active then we send a keepalive ping
* to make sure that the watchdog keep's running (and if
* possible that it takes the new timeout) */
@@ -237,6 +236,11 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
if (wdd->timeout == 0)
return -EOPNOTSUPP;
return put_user(wdd->timeout, p);
+ case WDIOC_GETTIMELEFT:
+ if (!wdd->ops->get_timeleft)
+ return -EOPNOTSUPP;
+
+ return put_user(wdd->ops->get_timeleft(wdd), p);
default:
return -ENOTTY;
}
@@ -347,7 +351,7 @@ int watchdog_dev_register(struct watchdog_device *watchdog)
/* Only one device can register for /dev/watchdog */
if (test_and_set_bit(0, &watchdog_dev_busy)) {
- pr_err("only one watchdog can use /dev/watchdog.\n");
+ pr_err("only one watchdog can use /dev/watchdog\n");
return -EBUSY;
}
@@ -355,8 +359,8 @@ int watchdog_dev_register(struct watchdog_device *watchdog)
err = misc_register(&watchdog_miscdev);
if (err != 0) {
- pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n",
- watchdog->info->identity, WATCHDOG_MINOR, err);
+ pr_err("%s: cannot register miscdev on minor=%d (err=%d)\n",
+ watchdog->info->identity, WATCHDOG_MINOR, err);
goto out;
}
@@ -383,8 +387,8 @@ int watchdog_dev_unregister(struct watchdog_device *watchdog)
/* We can only unregister the watchdog device that was registered */
if (watchdog != wdd) {
- pr_err("%s: watchdog was not registered as /dev/watchdog.\n",
- watchdog->info->identity);
+ pr_err("%s: watchdog was not registered as /dev/watchdog\n",
+ watchdog->info->identity);
return -ENODEV;
}
diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c
index 94ec22b..0a77655 100644
--- a/drivers/watchdog/wdrtas.c
+++ b/drivers/watchdog/wdrtas.c
@@ -26,6 +26,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -49,7 +51,7 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS_MISCDEV(TEMP_MINOR);
-static int wdrtas_nowayout = WATCHDOG_NOWAYOUT;
+static bool wdrtas_nowayout = WATCHDOG_NOWAYOUT;
static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0);
static char wdrtas_expect_close;
@@ -93,8 +95,8 @@ static int wdrtas_set_interval(int interval)
result = rtas_call(wdrtas_token_set_indicator, 3, 1, NULL,
WDRTAS_SURVEILLANCE_IND, 0, interval);
if (result < 0 && print_msg) {
- printk(KERN_ERR "wdrtas: setting the watchdog to %i "
- "timeout failed: %li\n", interval, result);
+ pr_err("setting the watchdog to %i timeout failed: %li\n",
+ interval, result);
print_msg--;
}
@@ -128,8 +130,8 @@ static int wdrtas_get_interval(int fallback_value)
spin_unlock(&rtas_data_buf_lock);
if (value[0] != 0 || value[1] != 2 || value[3] != 0 || result < 0) {
- printk(KERN_WARNING "wdrtas: could not get sp_spi watchdog "
- "timeout (%li). Continuing\n", result);
+ pr_warn("could not get sp_spi watchdog timeout (%li). Continuing\n",
+ result);
return fallback_value;
}
@@ -170,18 +172,18 @@ static void wdrtas_log_scanned_event(void)
int i;
for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16)
- printk(KERN_INFO "wdrtas: dumping event (line %i/%i), data = "
- "%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16),
- wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1],
- wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3],
- wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5],
- wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7],
- wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9],
- wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11],
- wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13],
- wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]);
+ pr_info("dumping event (line %i/%i), data = "
+ "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16),
+ wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1],
+ wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3],
+ wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5],
+ wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7],
+ wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9],
+ wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11],
+ wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13],
+ wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]);
}
/**
@@ -201,8 +203,7 @@ static void wdrtas_timer_keepalive(void)
(void *)__pa(wdrtas_logbuffer),
WDRTAS_LOGBUFFER_LEN);
if (result < 0)
- printk(KERN_ERR "wdrtas: event-scan failed: %li\n",
- result);
+ pr_err("event-scan failed: %li\n", result);
if (result == 0)
wdrtas_log_scanned_event();
} while (result == 0);
@@ -224,8 +225,7 @@ static int wdrtas_get_temperature(void)
result = rtas_get_sensor(WDRTAS_THERMAL_SENSOR, 0, &temperature);
if (result < 0)
- printk(KERN_WARNING "wdrtas: reading the thermal sensor "
- "failed: %i\n", result);
+ pr_warn("reading the thermal sensor failed: %i\n", result);
else
temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */
@@ -419,8 +419,7 @@ static int wdrtas_close(struct inode *inode, struct file *file)
if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR)
wdrtas_timer_stop();
else {
- printk(KERN_WARNING "wdrtas: got unexpected close. Watchdog "
- "not stopped.\n");
+ pr_warn("got unexpected close. Watchdog not stopped.\n");
wdrtas_timer_keepalive();
}
@@ -552,30 +551,24 @@ static int wdrtas_get_tokens(void)
{
wdrtas_token_get_sensor_state = rtas_token("get-sensor-state");
if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) {
- printk(KERN_WARNING "wdrtas: couldn't get token for "
- "get-sensor-state. Trying to continue without "
- "temperature support.\n");
+ pr_warn("couldn't get token for get-sensor-state. Trying to continue without temperature support.\n");
}
wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter");
if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) {
- printk(KERN_WARNING "wdrtas: couldn't get token for "
- "ibm,get-system-parameter. Trying to continue with "
- "a default timeout value of %i seconds.\n",
- WDRTAS_DEFAULT_INTERVAL);
+ pr_warn("couldn't get token for ibm,get-system-parameter. Trying to continue with a default timeout value of %i seconds.\n",
+ WDRTAS_DEFAULT_INTERVAL);
}
wdrtas_token_set_indicator = rtas_token("set-indicator");
if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) {
- printk(KERN_ERR "wdrtas: couldn't get token for "
- "set-indicator. Terminating watchdog code.\n");
+ pr_err("couldn't get token for set-indicator. Terminating watchdog code.\n");
return -EIO;
}
wdrtas_token_event_scan = rtas_token("event-scan");
if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) {
- printk(KERN_ERR "wdrtas: couldn't get token for event-scan. "
- "Terminating watchdog code.\n");
+ pr_err("couldn't get token for event-scan. Terminating watchdog code.\n");
return -EIO;
}
@@ -609,17 +602,14 @@ static int wdrtas_register_devs(void)
result = misc_register(&wdrtas_miscdev);
if (result) {
- printk(KERN_ERR "wdrtas: couldn't register watchdog misc "
- "device. Terminating watchdog code.\n");
+ pr_err("couldn't register watchdog misc device. Terminating watchdog code.\n");
return result;
}
if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) {
result = misc_register(&wdrtas_tempdev);
if (result) {
- printk(KERN_WARNING "wdrtas: couldn't register "
- "watchdog temperature misc device. Continuing "
- "without temperature support.\n");
+ pr_warn("couldn't register watchdog temperature misc device. Continuing without temperature support.\n");
wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE;
}
}
@@ -643,8 +633,7 @@ static int __init wdrtas_init(void)
return -ENODEV;
if (register_reboot_notifier(&wdrtas_notifier)) {
- printk(KERN_ERR "wdrtas: could not register reboot notifier. "
- "Terminating watchdog code.\n");
+ pr_err("could not register reboot notifier. Terminating watchdog code.\n");
wdrtas_unregister_devs();
return -ENODEV;
}
diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c
index d2ef002..ee4333c 100644
--- a/drivers/watchdog/wdt.c
+++ b/drivers/watchdog/wdt.c
@@ -32,6 +32,8 @@
* Matt Domsch : Added nowayout module option
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -46,7 +48,6 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#include "wd501p.h"
static unsigned long wdt_is_open;
@@ -65,8 +66,8 @@ MODULE_PARM_DESC(heartbeat,
"Watchdog heartbeat in seconds. (0 < heartbeat < 65536, default="
__MODULE_STRING(WD_TIMO) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -252,11 +253,11 @@ static int wdt_get_temperature(void)
static void wdt_decode_501(int status)
{
if (!(status & WDC_SR_TGOOD))
- printk(KERN_CRIT "Overheat alarm.(%d)\n", inb_p(WDT_RT));
+ pr_crit("Overheat alarm (%d)\n", inb_p(WDT_RT));
if (!(status & WDC_SR_PSUOVER))
- printk(KERN_CRIT "PSU over voltage.\n");
+ pr_crit("PSU over voltage\n");
if (!(status & WDC_SR_PSUUNDR))
- printk(KERN_CRIT "PSU under voltage.\n");
+ pr_crit("PSU under voltage\n");
}
/**
@@ -280,25 +281,25 @@ static irqreturn_t wdt_interrupt(int irq, void *dev_id)
spin_lock(&wdt_lock);
status = inb_p(WDT_SR);
- printk(KERN_CRIT "WDT status %d\n", status);
+ pr_crit("WDT status %d\n", status);
if (type == 501) {
wdt_decode_501(status);
if (tachometer) {
if (!(status & WDC_SR_FANGOOD))
- printk(KERN_CRIT "Possible fan fault.\n");
+ pr_crit("Possible fan fault\n");
}
}
if (!(status & WDC_SR_WCCR)) {
#ifdef SOFTWARE_REBOOT
#ifdef ONLY_TESTING
- printk(KERN_CRIT "Would Reboot.\n");
+ pr_crit("Would Reboot\n");
#else
- printk(KERN_CRIT "Initiating system reboot.\n");
+ pr_crit("Initiating system reboot\n");
emergency_restart();
#endif
#else
- printk(KERN_CRIT "Reset in 5ms.\n");
+ pr_crit("Reset in 5ms\n");
#endif
}
spin_unlock(&wdt_lock);
@@ -441,8 +442,7 @@ static int wdt_release(struct inode *inode, struct file *file)
wdt_stop();
clear_bit(0, &wdt_is_open);
} else {
- printk(KERN_CRIT
- "wdt: WDT device closed unexpectedly. WDT will not stop!\n");
+ pr_crit("WDT device closed unexpectedly. WDT will not stop!\n");
wdt_ping();
}
expect_close = 0;
@@ -593,7 +593,7 @@ static int __init wdt_init(void)
int ret;
if (type != 500 && type != 501) {
- printk(KERN_ERR "wdt: unknown card type '%d'.\n", type);
+ pr_err("unknown card type '%d'\n", type);
return -ENODEV;
}
@@ -601,53 +601,49 @@ static int __init wdt_init(void)
if not reset to the default */
if (wdt_set_heartbeat(heartbeat)) {
wdt_set_heartbeat(WD_TIMO);
- printk(KERN_INFO "wdt: heartbeat value must be "
- "0 < heartbeat < 65536, using %d\n", WD_TIMO);
+ pr_info("heartbeat value must be 0 < heartbeat < 65536, using %d\n",
+ WD_TIMO);
}
if (!request_region(io, 8, "wdt501p")) {
- printk(KERN_ERR
- "wdt: I/O address 0x%04x already in use\n", io);
+ pr_err("I/O address 0x%04x already in use\n", io);
ret = -EBUSY;
goto out;
}
ret = request_irq(irq, wdt_interrupt, 0, "wdt501p", NULL);
if (ret) {
- printk(KERN_ERR "wdt: IRQ %d is not free.\n", irq);
+ pr_err("IRQ %d is not free\n", irq);
goto outreg;
}
ret = register_reboot_notifier(&wdt_notifier);
if (ret) {
- printk(KERN_ERR
- "wdt: cannot register reboot notifier (err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
goto outirq;
}
if (type == 501) {
ret = misc_register(&temp_miscdev);
if (ret) {
- printk(KERN_ERR "wdt: cannot register miscdev "
- "on minor=%d (err=%d)\n", TEMP_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ TEMP_MINOR, ret);
goto outrbt;
}
}
ret = misc_register(&wdt_miscdev);
if (ret) {
- printk(KERN_ERR
- "wdt: cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto outmisc;
}
- printk(KERN_INFO "WDT500/501-P driver 0.10 "
- "at 0x%04x (Interrupt %d). heartbeat=%d sec (nowayout=%d)\n",
+ pr_info("WDT500/501-P driver 0.10 at 0x%04x (Interrupt %d). heartbeat=%d sec (nowayout=%d)\n",
io, irq, heartbeat, nowayout);
if (type == 501)
- printk(KERN_INFO "wdt: Fan Tachometer is %s\n",
- (tachometer ? "Enabled" : "Disabled"));
+ pr_info("Fan Tachometer is %s\n",
+ tachometer ? "Enabled" : "Disabled");
return 0;
outmisc:
diff --git a/drivers/watchdog/wdt285.c b/drivers/watchdog/wdt285.c
index f551356..5eec740 100644
--- a/drivers/watchdog/wdt285.c
+++ b/drivers/watchdog/wdt285.c
@@ -16,6 +16,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -32,6 +34,7 @@
#include <mach/hardware.h>
#include <asm/mach-types.h>
+#include <asm/system_info.h>
#include <asm/hardware/dec21285.h>
/*
@@ -49,7 +52,7 @@ static unsigned long timer_alive;
*/
static void watchdog_fire(int irq, void *dev_id)
{
- printk(KERN_CRIT "Watchdog: Would Reboot.\n");
+ pr_crit("Would Reboot\n");
*CSR_TIMER4_CNTL = 0;
*CSR_TIMER4_CLR = 0;
}
@@ -205,13 +208,11 @@ static int __init footbridge_watchdog_init(void)
if (retval < 0)
return retval;
- printk(KERN_INFO
- "Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n",
- soft_margin);
+ pr_info("Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n",
+ soft_margin);
if (machine_is_cats())
- printk(KERN_WARNING
- "Warning: Watchdog reset may not work on this machine.\n");
+ pr_warn("Warning: Watchdog reset may not work on this machine\n");
return 0;
}
diff --git a/drivers/watchdog/wdt977.c b/drivers/watchdog/wdt977.c
index a2f01c9..65a4023 100644
--- a/drivers/watchdog/wdt977.c
+++ b/drivers/watchdog/wdt977.c
@@ -23,6 +23,8 @@
* Netwinders only
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -37,13 +39,10 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#include <asm/mach-types.h>
#define WATCHDOG_VERSION "0.04"
#define WATCHDOG_NAME "Wdt977"
-#define PFX WATCHDOG_NAME ": "
-#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
#define IO_INDEX_PORT 0x370 /* on some systems it can be 0x3F0 */
#define IO_DATA_PORT (IO_INDEX_PORT + 1)
@@ -68,8 +67,8 @@ MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (60..15300, default="
module_param(testmode, int, 0);
MODULE_PARM_DESC(testmode, "Watchdog testmode (1 = no reboot), default=0");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -119,7 +118,7 @@ static int wdt977_start(void)
outb_p(LOCK_DATA, IO_INDEX_PORT);
spin_unlock_irqrestore(&spinlock, flags);
- printk(KERN_INFO PFX "activated.\n");
+ pr_info("activated\n");
return 0;
}
@@ -164,7 +163,7 @@ static int wdt977_stop(void)
outb_p(LOCK_DATA, IO_INDEX_PORT);
spin_unlock_irqrestore(&spinlock, flags);
- printk(KERN_INFO PFX "shutdown.\n");
+ pr_info("shutdown\n");
return 0;
}
@@ -288,8 +287,7 @@ static int wdt977_release(struct inode *inode, struct file *file)
clear_bit(0, &timer_alive);
} else {
wdt977_keepalive();
- printk(KERN_CRIT PFX
- "Unexpected close, not stopping watchdog!\n");
+ pr_crit("Unexpected close, not stopping watchdog!\n");
}
expect_close = 0;
return 0;
@@ -446,15 +444,14 @@ static int __init wd977_init(void)
{
int rc;
- printk(KERN_INFO PFX DRIVER_VERSION);
+ pr_info("driver v%s\n", WATCHDOG_VERSION);
/* Check that the timeout value is within its range;
if not reset to the default */
if (wdt977_set_timeout(timeout)) {
wdt977_set_timeout(DEFAULT_TIMEOUT);
- printk(KERN_INFO PFX
- "timeout value must be 60 < timeout < 15300, using %d\n",
- DEFAULT_TIMEOUT);
+ pr_info("timeout value must be 60 < timeout < 15300, using %d\n",
+ DEFAULT_TIMEOUT);
}
/* on Netwinder the IOports are already reserved by
@@ -462,9 +459,8 @@ static int __init wd977_init(void)
*/
if (!machine_is_netwinder()) {
if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) {
- printk(KERN_ERR PFX
- "I/O address 0x%04x already in use\n",
- IO_INDEX_PORT);
+ pr_err("I/O address 0x%04x already in use\n",
+ IO_INDEX_PORT);
rc = -EIO;
goto err_out;
}
@@ -472,22 +468,19 @@ static int __init wd977_init(void)
rc = register_reboot_notifier(&wdt977_notifier);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", rc);
+ pr_err("cannot register reboot notifier (err=%d)\n", rc);
goto err_out_region;
}
rc = misc_register(&wdt977_miscdev);
if (rc) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- wdt977_miscdev.minor, rc);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ wdt977_miscdev.minor, rc);
goto err_out_reboot;
}
- printk(KERN_INFO PFX
- "initialized. timeout=%d sec (nowayout=%d, testmode=%i)\n",
- timeout, nowayout, testmode);
+ pr_info("initialized. timeout=%d sec (nowayout=%d, testmode=%i)\n",
+ timeout, nowayout, testmode);
return 0;
diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c
index e0fc3baa..1c888c7 100644
--- a/drivers/watchdog/wdt_pci.c
+++ b/drivers/watchdog/wdt_pci.c
@@ -37,6 +37,8 @@
* Matt Domsch : nowayout module option
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -53,13 +55,10 @@
#include <linux/io.h>
#include <linux/uaccess.h>
-#include <asm/system.h>
#define WDT_IS_PCI
#include "wd501p.h"
-#define PFX "wdt_pci: "
-
/* We can only use 1 card due to the /dev/watchdog restriction */
static int dev_count;
@@ -80,8 +79,8 @@ MODULE_PARM_DESC(heartbeat,
"Watchdog heartbeat in seconds. (0<heartbeat<65536, default="
__MODULE_STRING(WD_TIMO) ")");
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -312,33 +311,32 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id)
status = inb(WDT_SR);
udelay(8);
- printk(KERN_CRIT PFX "status %d\n", status);
+ pr_crit("status %d\n", status);
if (type == 501) {
if (!(status & WDC_SR_TGOOD)) {
- printk(KERN_CRIT PFX "Overheat alarm.(%d)\n",
- inb(WDT_RT));
+ pr_crit("Overheat alarm (%d)\n", inb(WDT_RT));
udelay(8);
}
if (!(status & WDC_SR_PSUOVER))
- printk(KERN_CRIT PFX "PSU over voltage.\n");
+ pr_crit("PSU over voltage\n");
if (!(status & WDC_SR_PSUUNDR))
- printk(KERN_CRIT PFX "PSU under voltage.\n");
+ pr_crit("PSU under voltage\n");
if (tachometer) {
if (!(status & WDC_SR_FANGOOD))
- printk(KERN_CRIT PFX "Possible fan fault.\n");
+ pr_crit("Possible fan fault\n");
}
}
if (!(status & WDC_SR_WCCR)) {
#ifdef SOFTWARE_REBOOT
#ifdef ONLY_TESTING
- printk(KERN_CRIT PFX "Would Reboot.\n");
+ pr_crit("Would Reboot\n");
#else
- printk(KERN_CRIT PFX "Initiating system reboot.\n");
+ pr_crit("Initiating system reboot\n");
emergency_restart(NULL);
#endif
#else
- printk(KERN_CRIT PFX "Reset in 5ms.\n");
+ pr_crit("Reset in 5ms\n");
#endif
}
spin_unlock(&wdtpci_lock);
@@ -484,7 +482,7 @@ static int wdtpci_release(struct inode *inode, struct file *file)
if (expect_close == 42) {
wdtpci_stop();
} else {
- printk(KERN_CRIT PFX "Unexpected close, not stopping timer!");
+ pr_crit("Unexpected close, not stopping timer!\n");
wdtpci_ping();
}
expect_close = 0;
@@ -614,29 +612,29 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev,
dev_count++;
if (dev_count > 1) {
- printk(KERN_ERR PFX "This driver only supports one device\n");
+ pr_err("This driver only supports one device\n");
return -ENODEV;
}
if (type != 500 && type != 501) {
- printk(KERN_ERR PFX "unknown card type '%d'.\n", type);
+ pr_err("unknown card type '%d'\n", type);
return -ENODEV;
}
if (pci_enable_device(dev)) {
- printk(KERN_ERR PFX "Not possible to enable PCI Device\n");
+ pr_err("Not possible to enable PCI Device\n");
return -ENODEV;
}
if (pci_resource_start(dev, 2) == 0x0000) {
- printk(KERN_ERR PFX "No I/O-Address for card detected\n");
+ pr_err("No I/O-Address for card detected\n");
ret = -ENODEV;
goto out_pci;
}
if (pci_request_region(dev, 2, "wdt_pci")) {
- printk(KERN_ERR PFX "I/O address 0x%llx already in use\n",
- (unsigned long long)pci_resource_start(dev, 2));
+ pr_err("I/O address 0x%llx already in use\n",
+ (unsigned long long)pci_resource_start(dev, 2));
goto out_pci;
}
@@ -645,53 +643,48 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev,
if (request_irq(irq, wdtpci_interrupt, IRQF_SHARED,
"wdt_pci", &wdtpci_miscdev)) {
- printk(KERN_ERR PFX "IRQ %d is not free\n", irq);
+ pr_err("IRQ %d is not free\n", irq);
goto out_reg;
}
- printk(KERN_INFO
- "PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%llx (Interrupt %d)\n",
- (unsigned long long)io, irq);
+ pr_info("PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%llx (Interrupt %d)\n",
+ (unsigned long long)io, irq);
/* Check that the heartbeat value is within its range;
if not reset to the default */
if (wdtpci_set_heartbeat(heartbeat)) {
wdtpci_set_heartbeat(WD_TIMO);
- printk(KERN_INFO PFX
- "heartbeat value must be 0 < heartbeat < 65536, using %d\n",
- WD_TIMO);
+ pr_info("heartbeat value must be 0 < heartbeat < 65536, using %d\n",
+ WD_TIMO);
}
ret = register_reboot_notifier(&wdtpci_notifier);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register reboot notifier (err=%d)\n", ret);
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
goto out_irq;
}
if (type == 501) {
ret = misc_register(&temp_miscdev);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- TEMP_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ TEMP_MINOR, ret);
goto out_rbt;
}
}
ret = misc_register(&wdtpci_miscdev);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
+ pr_err("cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
goto out_misc;
}
- printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
+ pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
heartbeat, nowayout);
if (type == 501)
- printk(KERN_INFO "wdt: Fan Tachometer is %s\n",
- (tachometer ? "Enabled" : "Disabled"));
+ pr_info("Fan Tachometer is %s\n",
+ tachometer ? "Enabled" : "Disabled");
ret = 0;
out:
diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c
index 263c883..b1815c5 100644
--- a/drivers/watchdog/wm831x_wdt.c
+++ b/drivers/watchdog/wm831x_wdt.c
@@ -22,8 +22,8 @@
#include <linux/mfd/wm831x/pdata.h>
#include <linux/mfd/wm831x/watchdog.h>
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
@@ -163,6 +163,8 @@ static int wm831x_wdt_set_timeout(struct watchdog_device *wdt_dev,
ret);
}
+ wdt_dev->timeout = timeout;
+
return ret;
}
diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c
index 5d7113c..3c76693 100644
--- a/drivers/watchdog/wm8350_wdt.c
+++ b/drivers/watchdog/wm8350_wdt.c
@@ -8,63 +8,65 @@
* as published by the Free Software Foundation
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#include <linux/uaccess.h>
#include <linux/mfd/wm8350/core.h>
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-static unsigned long wm8350_wdt_users;
-static struct miscdevice wm8350_wdt_miscdev;
-static int wm8350_wdt_expect_close;
static DEFINE_MUTEX(wdt_mutex);
static struct {
- int time; /* Seconds */
- u16 val; /* To be set in WM8350_SYSTEM_CONTROL_2 */
+ unsigned int time; /* Seconds */
+ u16 val; /* To be set in WM8350_SYSTEM_CONTROL_2 */
} wm8350_wdt_cfgs[] = {
{ 1, 0x02 },
{ 2, 0x04 },
{ 4, 0x05 },
};
-static struct wm8350 *get_wm8350(void)
-{
- return dev_get_drvdata(wm8350_wdt_miscdev.parent);
-}
-
-static int wm8350_wdt_set_timeout(struct wm8350 *wm8350, u16 value)
+static int wm8350_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int timeout)
{
- int ret;
+ struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
+ int ret, i;
u16 reg;
+ for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
+ if (wm8350_wdt_cfgs[i].time == timeout)
+ break;
+ if (i == ARRAY_SIZE(wm8350_wdt_cfgs))
+ return -EINVAL;
+
mutex_lock(&wdt_mutex);
wm8350_reg_unlock(wm8350);
reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
reg &= ~WM8350_WDOG_TO_MASK;
- reg |= value;
+ reg |= wm8350_wdt_cfgs[i].val;
ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
wm8350_reg_lock(wm8350);
mutex_unlock(&wdt_mutex);
+ wdt_dev->timeout = timeout;
return ret;
}
-static int wm8350_wdt_start(struct wm8350 *wm8350)
+static int wm8350_wdt_start(struct watchdog_device *wdt_dev)
{
+ struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
int ret;
u16 reg;
@@ -82,8 +84,9 @@ static int wm8350_wdt_start(struct wm8350 *wm8350)
return ret;
}
-static int wm8350_wdt_stop(struct wm8350 *wm8350)
+static int wm8350_wdt_stop(struct watchdog_device *wdt_dev)
{
+ struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
int ret;
u16 reg;
@@ -100,8 +103,9 @@ static int wm8350_wdt_stop(struct wm8350 *wm8350)
return ret;
}
-static int wm8350_wdt_kick(struct wm8350 *wm8350)
+static int wm8350_wdt_ping(struct watchdog_device *wdt_dev)
{
+ struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
int ret;
u16 reg;
@@ -115,168 +119,25 @@ static int wm8350_wdt_kick(struct wm8350 *wm8350)
return ret;
}
-static int wm8350_wdt_open(struct inode *inode, struct file *file)
-{
- struct wm8350 *wm8350 = get_wm8350();
- int ret;
-
- if (!wm8350)
- return -ENODEV;
-
- if (test_and_set_bit(0, &wm8350_wdt_users))
- return -EBUSY;
-
- ret = wm8350_wdt_start(wm8350);
- if (ret != 0)
- return ret;
-
- return nonseekable_open(inode, file);
-}
-
-static int wm8350_wdt_release(struct inode *inode, struct file *file)
-{
- struct wm8350 *wm8350 = get_wm8350();
-
- if (wm8350_wdt_expect_close)
- wm8350_wdt_stop(wm8350);
- else {
- dev_warn(wm8350->dev, "Watchdog device closed uncleanly\n");
- wm8350_wdt_kick(wm8350);
- }
-
- clear_bit(0, &wm8350_wdt_users);
-
- return 0;
-}
-
-static ssize_t wm8350_wdt_write(struct file *file,
- const char __user *data, size_t count,
- loff_t *ppos)
-{
- struct wm8350 *wm8350 = get_wm8350();
- size_t i;
-
- if (count) {
- wm8350_wdt_kick(wm8350);
-
- if (!nowayout) {
- /* In case it was set long ago */
- wm8350_wdt_expect_close = 0;
-
- /* scan to see whether or not we got the magic
- character */
- for (i = 0; i != count; i++) {
- char c;
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- wm8350_wdt_expect_close = 42;
- }
- }
- }
- return count;
-}
-
-static const struct watchdog_info ident = {
+static const struct watchdog_info wm8350_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "WM8350 Watchdog",
};
-static long wm8350_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct wm8350 *wm8350 = get_wm8350();
- int ret = -ENOTTY, time, i;
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- u16 reg;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
- break;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- ret = put_user(0, p);
- break;
-
- case WDIOC_SETOPTIONS:
- {
- int options;
-
- if (get_user(options, p))
- return -EFAULT;
-
- ret = -EINVAL;
-
- /* Setting both simultaneously means at least one must fail */
- if (options == WDIOS_DISABLECARD)
- ret = wm8350_wdt_stop(wm8350);
-
- if (options == WDIOS_ENABLECARD)
- ret = wm8350_wdt_start(wm8350);
- break;
- }
-
- case WDIOC_KEEPALIVE:
- ret = wm8350_wdt_kick(wm8350);
- break;
-
- case WDIOC_SETTIMEOUT:
- ret = get_user(time, p);
- if (ret)
- break;
-
- if (time == 0) {
- if (nowayout)
- ret = -EINVAL;
- else
- wm8350_wdt_stop(wm8350);
- break;
- }
-
- for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
- if (wm8350_wdt_cfgs[i].time == time)
- break;
- if (i == ARRAY_SIZE(wm8350_wdt_cfgs))
- ret = -EINVAL;
- else
- ret = wm8350_wdt_set_timeout(wm8350,
- wm8350_wdt_cfgs[i].val);
- break;
-
- case WDIOC_GETTIMEOUT:
- reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
- reg &= WM8350_WDOG_TO_MASK;
- for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
- if (wm8350_wdt_cfgs[i].val == reg)
- break;
- if (i == ARRAY_SIZE(wm8350_wdt_cfgs)) {
- dev_warn(wm8350->dev,
- "Unknown watchdog configuration: %x\n", reg);
- ret = -EINVAL;
- } else
- ret = put_user(wm8350_wdt_cfgs[i].time, p);
-
- }
-
- return ret;
-}
-
-static const struct file_operations wm8350_wdt_fops = {
+static const struct watchdog_ops wm8350_wdt_ops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = wm8350_wdt_write,
- .unlocked_ioctl = wm8350_wdt_ioctl,
- .open = wm8350_wdt_open,
- .release = wm8350_wdt_release,
+ .start = wm8350_wdt_start,
+ .stop = wm8350_wdt_stop,
+ .ping = wm8350_wdt_ping,
+ .set_timeout = wm8350_wdt_set_timeout,
};
-static struct miscdevice wm8350_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &wm8350_wdt_fops,
+static struct watchdog_device wm8350_wdt = {
+ .info = &wm8350_wdt_info,
+ .ops = &wm8350_wdt_ops,
+ .timeout = 4,
+ .min_timeout = 1,
+ .max_timeout = 4,
};
static int __devinit wm8350_wdt_probe(struct platform_device *pdev)
@@ -288,18 +149,18 @@ static int __devinit wm8350_wdt_probe(struct platform_device *pdev)
return -ENODEV;
}
- /* Default to 4s timeout */
- wm8350_wdt_set_timeout(wm8350, 0x05);
+ watchdog_set_nowayout(&wm8350_wdt, nowayout);
+ watchdog_set_drvdata(&wm8350_wdt, wm8350);
- wm8350_wdt_miscdev.parent = &pdev->dev;
+ /* Default to 4s timeout */
+ wm8350_wdt_set_timeout(&wm8350_wdt, 4);
- return misc_register(&wm8350_wdt_miscdev);
+ return watchdog_register_device(&wm8350_wdt);
}
static int __devexit wm8350_wdt_remove(struct platform_device *pdev)
{
- misc_deregister(&wm8350_wdt_miscdev);
-
+ watchdog_unregister_device(&wm8350_wdt);
return 0;
}
diff --git a/drivers/watchdog/xen_wdt.c b/drivers/watchdog/xen_wdt.c
index 49bd9d3..e4a25b5 100644
--- a/drivers/watchdog/xen_wdt.c
+++ b/drivers/watchdog/xen_wdt.c
@@ -9,9 +9,10 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define DRV_NAME "wdt"
#define DRV_VERSION "0.01"
-#define PFX DRV_NAME ": "
#include <linux/bug.h>
#include <linux/errno.h>
@@ -131,16 +132,17 @@ static int xen_wdt_open(struct inode *inode, struct file *file)
static int xen_wdt_release(struct inode *inode, struct file *file)
{
+ int err = 0;
+
if (expect_release)
- xen_wdt_stop();
+ err = xen_wdt_stop();
else {
- printk(KERN_CRIT PFX
- "unexpected close, not stopping watchdog!\n");
+ pr_crit("unexpected close, not stopping watchdog!\n");
xen_wdt_kick();
}
- is_active = false;
+ is_active = err;
expect_release = false;
- return 0;
+ return err;
}
static ssize_t xen_wdt_write(struct file *file, const char __user *data,
@@ -251,30 +253,27 @@ static int __devinit xen_wdt_probe(struct platform_device *dev)
case -EINVAL:
if (!timeout) {
timeout = WATCHDOG_TIMEOUT;
- printk(KERN_INFO PFX
- "timeout value invalid, using %d\n", timeout);
+ pr_info("timeout value invalid, using %d\n", timeout);
}
ret = misc_register(&xen_wdt_miscdev);
if (ret) {
- printk(KERN_ERR PFX
- "cannot register miscdev on minor=%d (%d)\n",
+ pr_err("cannot register miscdev on minor=%d (%d)\n",
WATCHDOG_MINOR, ret);
break;
}
- printk(KERN_INFO PFX
- "initialized (timeout=%ds, nowayout=%d)\n",
- timeout, nowayout);
+ pr_info("initialized (timeout=%ds, nowayout=%d)\n",
+ timeout, nowayout);
break;
case -ENOSYS:
- printk(KERN_INFO PFX "not supported\n");
+ pr_info("not supported\n");
ret = -ENODEV;
break;
default:
- printk(KERN_INFO PFX "bogus return value %d\n", ret);
+ pr_info("bogus return value %d\n", ret);
break;
}
@@ -299,11 +298,18 @@ static void xen_wdt_shutdown(struct platform_device *dev)
static int xen_wdt_suspend(struct platform_device *dev, pm_message_t state)
{
- return xen_wdt_stop();
+ typeof(wdt.id) id = wdt.id;
+ int rc = xen_wdt_stop();
+
+ wdt.id = id;
+ return rc;
}
static int xen_wdt_resume(struct platform_device *dev)
{
+ if (!wdt.id)
+ return 0;
+ wdt.id = 0;
return xen_wdt_start();
}
@@ -326,7 +332,7 @@ static int __init xen_wdt_init_module(void)
if (!xen_domain())
return -ENODEV;
- printk(KERN_INFO PFX "Xen WatchDog Timer Driver v%s\n", DRV_VERSION);
+ pr_info("Xen WatchDog Timer Driver v%s\n", DRV_VERSION);
err = platform_driver_register(&xen_wdt_driver);
if (err)
@@ -346,7 +352,7 @@ static void __exit xen_wdt_cleanup_module(void)
{
platform_device_unregister(platform_device);
platform_driver_unregister(&xen_wdt_driver);
- printk(KERN_INFO PFX "module unloaded\n");
+ pr_info("module unloaded\n");
}
module_init(xen_wdt_init_module);